C语言提高

数据类型与变量

  1. 数据类型
    • 类型相同的数据具有相同的表示形式、存储格式和操作,程序中所以数据必须属于某种数据类型。
    • 为编译器提供分配内存大小的依据,数据类型本身没有内存空间。
    • 使用typedef给类型起别名方便使用。
    • void:无类型,用来定义指针 void *p,函数返回值,函数参数。
#include <stdio.h>

int main(int argc, char const *argv[])
{
  int arr[] = {1, 2, 3};
  printf("%d\n", sizeof(arr)); // 12

  // arr = 6422308 arr + 1 = 6422312
  // arr 表示数据手元素地址 + 1 加的是一个元素的长度4
  printf("arr = %d arr + 1 = %d\n", arr, arr + 1);

  // &arr = 6422308 &arr + 1 = 6422320
  // &arr 表示的是整个数组的首地址 + 1,加整个数组的长度12
  printf("&arr = %d &arr + 1 = %d\n", &arr, &arr + 1); 
  return 0;
}
  1. 变量:变量的使用要抓住生命周期和作用域两点。
    • 全局变量(包括静态(文件内使用),普通(可跨文件使用))。
    • 局部变量(函数中的变量,形参,函数内使用)。
    • 生命周期伴随程序结束:静态变量(包括全局和局部,存放在data区),全局变量。
    • 函数执行完成销毁:局部变量,形参。
    • 有些局部变量指向堆区地址,在函数执行结束时,形参销毁但是堆区地址不会自动回收,只有free()函数回收。

内存分区(stack、heap、data、bss、text)

  1. stack:栈区,存放函数执行过程中的局部变量和形参,函数调用完毕释放。
    • 栈区地址由高向低使用,栈区有一定的大小限制,超过会造成堆栈溢出问题。

指针、函数指针

  1. 数组在作为形参时会退化为指针,在接收数组参数时,形参可以直接写指针形式。
#include <stdio.h>

int func(int arr[]){
  return sizeof(arr);
}

int func1(int arr[3]){
  return sizeof(arr);
}

int func2(int *arr){
  return sizeof(arr);
}

int main(int argc, char const *argv[])
{
  int arr[3] = {1, 2, 3};
  printf("数组的长度为:%d\n", sizeof(arr));  // 12(int * 3)

  printf("func 数组形参的长度为:%d\n", func(arr)); // 4 32位系统中一个指针的长度

  printf("func1 数组形参的长度为:%d\n", func1(arr)); // 4

  printf("func2 数组形参的长度为:%d\n", func2(arr)); // 4
  return 0;
}
  1. 指针是大小固定(与编译器决定)的数据类型。
    • 指针变量存储一个地址,它通过这个地址访问对应的地址块。
  2. 二级指针做形参,接收一个一级指针的地址。
    • 在使用指针类型形参时,要先做判断,防止段错误。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void func(char **p, int *len){
  if(p == NULL || len == NULL){
    return;
  }
  char *temp = (char *)malloc(100);
  strcpy(temp, "hgzzz");
  *p = temp;
  *len = strlen(temp);
}

int main(int argc, char const *argv[])
{
  char *p = NULL;
  int len = 0;
  func(&p, &len);
  printf("%s  %d\n", p, len); // hgzzz  5
  return 0;
}

字符串处理

  1. 函数接收字符串数组。
#include <stdio.h>
#include <string.h>

void print_arr(char **str_arr ,int len){
  if(str_arr == NULL){
    return;
  }
  int i;
  for(i = 0; i < len; i++){
    printf("%s ", str_arr[i]);
  }
}

void sort_arr(char **str_arr ,int len){
  if(str_arr == NULL) {
    return;
  }
  int i,j;
  char *temp;
  for(i = 0; i < len - 1; i++){ // 快速排序
    for(j = i + 1; j < len; j++){
      if(strcmp(str_arr[i], str_arr[j]) > 0){
        temp = str_arr[i];
        str_arr[i] = str_arr[j];
        str_arr[j] = temp;
      }
    }
  }
}


int main(int argc, char const *argv[])
{
  char *str_arr[] = { "aaa", "vvv", "ccc", "bbb" };
  int len = sizeof(str_arr) / sizeof(str_arr[0]);

  printf("排序前\n"); // aaa vvv ccc bbb 
  print_arr(str_arr, len);

  sort_arr(str_arr, len);

  printf("\n排序后\n"); // aaa bbb ccc vvv 
  print_arr(str_arr, len);
  printf("\n");
  return 0;
}

sizeof()

  1. sizeof()是运算符,不是函数,sizeof()部分在编译之前就已经确定结果。
#include  <stdio.h>

int main(int argc, char const *argv[])
{
  int a;
  printf("int_len = %d\n", sizeof(a)); // int_len = 4

  char *p;
  printf("pointer_len = %d\n",sizeof(p)); // pointer_len = 4

  return 0;
}
  1. sizeof()可以求得void类型指针的长度,但是无法求得void类型变量的长度。因为这样的变量无法分配内存大小,所以你也定义不了。但是指针是存储的一个地址,地址是根据编译器的位数决定的,所以指针的大小是确定的,比如32位编译器分配的指针就是4个字节的大小。
#include  <stdio.h>

int main(int argc, char const *argv[])
{
  /* 
  void a; // error: variable or field 'a' declared void
  printf("void_variable_len = %d\n", sizeof(a));
  */
  void *p;
  printf("void_pointer_len = %d\n", sizeof(p)); // void_pointer_len = 4
}
  1. sizeof()求静态分配内存的数组的大小
#include <stdio.h>

int func(int arr[2]){
  return sizeof(arr);
}

int main(int argc, char const *argv[])
{
  int int_arr[2] = {0};
  printf("int_arr_len = %d\n", sizeof(int_arr)); // int_arr_len = 8

  char char_arr[3] = {'a', 'b', 'c'};
  printf("char_arr_len = %d\n", sizeof(char_arr)); // char_arr_len = 3

  // 字符串数组,默认结尾加上结束符'\0'
  char str_arr[] = "hgz";
  printf("str_arr_len = %d\n", sizeof(str_arr)); // str_arr_len = 4

  // 数组作为函数参数,形参退化成指针,提高运行效率
  printf("arr_arg_len = %d\n", func(int_arr)); // arr_arg_len = 4
  return 0;
}

数组

  1. 指针数组,数组里面存储指针类型的变量。
#include <stdio.h>

int main(int argc, char const *argv[])
{
  int i = 0;
  // 指针数组,数组里面存储指针类型
  char *str_arr[] = {"aaa", "bbb", "ccc"};
  for(i = 0; i < sizeof(str_arr)/sizeof(str_arr[0]); i++){
    printf("%s\n", str_arr[i]);
    printf("%d\n", sizeof(str_arr[i])); // 4
  }

  return 0;
}
  1. 数组指针,指向整个数组的指针,而不是首元素。
#include <stdio.h>

int main(int argc, char const *argv[])
{
  int arr[10] = {0};

  typedef int int_arr_10[10];
  int_arr_10 *p = NULL;
  p = &arr;
  // p = 6422276 p + 1 = 6422316 步长是一个数组的大小
  printf("p = %d p + 1 = %d\n", p, p+1);

  // 定义数组指针类型
  int(*q)[10];
  q = &arr;
  printf("%d\n", q);
  return 0;
}
  1. 二维数组
#include <stdio.h>

int main(int argc, char const *argv[])
{
  int arr[][2] = {{1, 2}, {3, 4}, {5, 6}};
  // 差8个字节,步长为一行
  printf("arr = %d arr+1 = %d\n", arr, arr+1); 
  // 差24字节,步长为整个二维数组的长度
  printf("&arr = %d &arr+1 = %d\n", &arr, &arr+1); 
  return 0;
}