C语言基础总结Part

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u012886093/article/details/90638102

### 数据类型的本质

> 数据类型可理解为创建变量的模具(模子);是固定大小内存的别名.

> 变量本质:( 一段连续)内存空间的别名、内存空间的标号.

> 数据类型的作用 :编译器预算对象(变量)分配的内存;也间接指出了变量的解析方式.

### 指针铁律

a.指针是一种数据类型,是指向它指向的内存空间的数据类型.

> 指针也是一种变量 ,占用内存空间,保存内存地址.<指针变量与它所指向内存空间是两个不同的概念>

& 站在编译器的角度,形参如果是指针类型,C编译器只会分配4个字节的内存.

void function(char ***pt);

& 当我们使用指针的时候,我们才关心这个内存块是一维的,还是二维的。

> 指针指向谁,就把谁的地址赋给指针: pt = &a;

> 指针的步长,根据它所指向的内存空间的类型来决定的.<类型决定解析方式>

b.通过*pt来访问和修改实参的值是指针存在的最大意义

& 在函数调用的时候

用1级指针形参,去间接修改 0级指针(实参)的值。

用2级指针形参,去间接修改 1级指针(实参)的值。

用3级指针形参,去间接修改 2级指针(实参)的值。

用n级指针形参,去间接修改 n-1级指针(实参)的值。

c.辅助指针:处理业务逻辑时常添加一个或两个辅助指针(px,py)来解决问题

uint8_t *pt = (uint8_t *)[data bytes];// uint8_t占1个字节.

### 函数指针与指针函数

void (*foo)(int num);//指向参数为int,返回值为void的函数的指针,函数指针

void func(int);//func是函数的入口地址;&func是函数指针,类型是void(*)(int)

void *foo(int num);//指针函数,返回值为指针的函数

const int *pci; //指向const int的指针就是指向整数常量的指针

int* const pci; //指向int型变量的常量指针,指针地址不可修改.

### 数组指针和指针数组

int (*p)[10] ;//数组指针,p指向的是一个带10个int型元素的数组.

int* arr[5];//指针数组,数组中每一个元素都是指针.

a[i] 等价于 *(a+i);//数组的本质是一段连续的存储空间;

1)数组首元素的地址和数组地址是两个不同的概念

2)数组名代表数组首元素的地址,它是个常量

3)数组首元素的地址和数组的地址值相等

比如int a[10] => a是数组首元素的地址;&a是数组指针,类型是int(*)[10].//&a+1按照步长去理解

typedef int MyArrayType[10];//定义一个int a[10]的数组类型

typedef int (*MyPArrayType)[10];//定义一个指针类型,定义一个指向数组(int a[10])的指针类型.

扩展: int myarr[3][5];# myarr是一个数组指针。

理解: *(*(myarr+i)+j);// *(myarr+i)表示到第几行,记住指针的步长就ok.

### 数组作函数的参数

> C语言中只会以机械式的值拷贝的方式传递参数(实参把值传给形参)

数组参数                           等效的指针参数

一维数组 char a[30] <=>     指针 char*

指针数组 char *a[30] <=>  指针的指针 char **a

二维数组 char a[10][30] <=> 数组指针 char(*a)[30]

& 当数组作函数参数的时候会退化为指针.

& 总结:函数调用的时候,把数组首地址和有效数据长度传给被调函数才是最正确的做法.

### 二级指针的3种内存模型

a.第一种内存模型

// char *arr[] = {"cmc","abc","ycs","1111"};

// arr是个二级指针,和int a[10] 级指针不一样,sizeof(arr)=32; []相当于一个指针

void printArray(char **arr,int len){ for (int i = 0; i < len; i++) {

printf("%s,",arr[i]);// *(arr+i) }

}

b.第二种内存模型

// char pArray[10][30] = {"cmc","abc","ycs","1111"};

int printAarray02(char pArray[10][30], int num)

{

for (int i=0; i<num; i++) {

printf("%s \n", pArray[i]); }

return 0; }

c.第三种内存模型

char **pArray = (char**)malloc(10*sizeof(char*));

for (int i = 0; i < 10; i++) {

pArray[i] = (char *)malloc(16*sizeof(char));//分别赋值,可以让字符串长度不一样

}

for (int i = 0; i < 10; i++) {

free(pArray[i]);//释放内存 }

free(pArray);

### 多维数组的本质

// arr的本质是一个数组指针,每次往后跳一维的维数,这里是5*4个字节

int arr[3][5];// arr+i相当于跳到第i行

int (*pArray)[5];

pArray = arr;

int temp = 0;

for (int i=0; i<3; i++) {

for (int j=0; j<5; j++) {

pArray[i][j] = ++temp;

} }

### 数组指针的使用

typedef int MyArrayType[5];

int a[5];

MyArrayType *pArray = &a;//定义一个数组类型的指针

//我通过数组指针的方式来操作a[5]这块内存

for (int i=0; i<5; i++)

{

(*pArray)[i] = i+1; //a[i] = i+1;

}

### 结构体内存对齐

字节对齐的细节和编译器实现相关,但一般而言,满足三个准则:
1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
2) 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding);
3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节(trailing padding)。

**说明:

字节对齐第3条准则提及最宽基本类型的概念,所谓基本类型是指像char、short、int、float、double这样的内置数据类型。“数据宽度”就是指其sizeof的大小。诸如结构体、共用体和数组等都不是基本数据类型。除结构体的第一个成员外,其他所有的成员的地址相对于结构体地址(即它首个成员的地址)的偏移量必须为实际对齐单位或自身大小的整数倍(取两者中小的那个)

struct A
{
    char c;
    short a;
    int i;    
}; 

sizeof(A) = 8;

猜你喜欢

转载自blog.csdn.net/u012886093/article/details/90638102