C:
基本数据类型:
char(1)、short(2)、int(4)、float(4)、long(8)、double(8)
void
enum
指针(4) int *a; double *b; void *c 都是4字节(32位)(64位 8字节)
void * p; 万能指针
结构体(4)
常用方法:
sizeof(xxx) 返回当前对象的字节数
typedef 给类型起别名,常用与结构体使用,如:
typedef unsigned int u32;
u32 a = 1;
typedef struct MyStruct{
int a;
int b;
}TMP;
struct TMP t1;
注意点:
-
数组作为函数参数,则数组会退化为指针,如
void m1(int a[1], int n) {}
void m1(int a[100], int n) {}
void m1(int *a, int n) {}
三者等同,都是数组a的元素首地址、数组首地址,数字无意义 -
类型+1的变化
int b[10]; //分配 4 * 10 = 40字节空间
输出 b, &b ;两个返回值相同
b: 数组首元素地址
&b:数组首地址
如果输出 b+1, &b+1,此时返回结果不同
原因:b表示的是数组元素首地址为int类型,4字节,返回比b多4字节; &b表示数组首地址,为数组类型,返回比&b多40字节
内存四区:
堆:主动分配释放(动态内存操作与释放),如果不主动释放,可能由操作系统回收
栈:由编译器自动分配释放,存储局部变量
全局区:全局变量、静态变量、常量区。
全局变量和静态变量的存储放在一起,初始化的全局变量和静态变量在一块区域,未初始化的在另一块区域,全局区由操作系统管理。
代码区:存放函数的二进制代码。操作系统管理
char str[] = "asdfasfdsafd";
在全局区定义一组字符串,内部编译器会将单个字符复制到str数组内
char *str = "adfasfasfsf";
这里是指针的指向,而不是复制
char *str = (char *)malloc(100);
这里在堆中分配内存100个字节
栈的分配方向
高地址
|
|
|
|
低地址
栈的分配从高到低,堆的分配从低到高
数组内部分配 从低到高
理解点1
char *c = "abcdefg";
c[1] = "1";
和
char c[] = "abcdefg";
c[1] = "1"
第一个会运行报错,因为c指针指向文字常量区,不可直接修改
第二个则是把文字常量区的内容拷贝到栈中的数组中,可以直接修改
理解点2 -- 通过指针间接赋值
int a = 10;
int *p = NULL;
p = &a;
*p = 8;
如果想通过函数形参改变实参的值,必须传地址
- 值传递,形参的任何修改不会影响到实参
- 地址传递,形参(通过*操作符)的任意修改会影响到实参
理解点3
int a = 10;
int *p = NULL;
p = &a; //指针指向谁,就把谁的地址赋值给指针
*p = 22; //*在左边,给内存区域赋值,写内存
int b = *p; //*在右边,读内存区域的值,读内存
int **q = &p; //&表示读取变量的地址,给q指针,因为p已经是一级指针,q就需要是二级指针。
char *p = NULL;
char buf[] = "abcdef";
p = buf; //改变指针变量的值
p = p + 1; //改变了指向变量的值,改变了指针的指向
*p = 'm'; //改变指针指向的内存,并不会影响到指针的值
理解点4
int fun1() {
int a = 10;
return a;
}
int *fun2() {
int a = 10;
return &a;
}
int * fun3() {
static int a = 10;
return &a;
}
这里只有fun2是无意义的方法,本身方法执行没有问题,外部指针会被赋值为a的地址,当fun2结束之后,a的栈内存地址会被回收,外部指针将指向一个非法的内存地址。
fun3因为a是静态变量,保存在全局区,不受方法栈回收的影响,
理解点5
void fun1(char *p) {
p = (char *)malloc(100);
strcpy(p, "abcdefg");
}
void fun2(char **p) {
char *tmp = (char *)malloc(199);
if(tmp == NULL) return -1;
strcpy(tmp, "abcdefg");
*p = tmp;
}
void main(xxx) {
char *p = NULL;
fun1(p);
//输出p 是 NULL
fun2(&p);
//输出p 是abcdefg
}
fun1的调用因为是值传递,函数内部的修改对外部变量无效
fun2的调用是地址传递,属于指针的间接赋值
指针数组
char *p1 = "abc";
char *p2 = "456";
char *p[] = {"123", "789", p1, p2};
void fun(char *p[]) {}
void fun(char **p) {}
//两者在使用上一致,区别在于**p只能指向一个地址,数组内可以存多个地址
// char **p = {"abc", "efg", "123"}; //这种写法错误,因为一个指针无法指向多个地址
二维数组
char[4][10] a = {"aaaa","bbbb","cccc","dddd"};
//char[][10] a = {"aaaa","bbbb","cccc","dddd"}; 等同
//char[][10] a; 会报错,第一维不定义时,必须要初始化
//a代表首行地址,首行地址和首行首元素地址有区别,但值相同,区别在于步长不同,这里首行+1跨越10个字节,首元素+1跨越1个字节
int a[][4] = {1,2,3,4,5,6,7,8,9,10,11,12}
// a: 代表首行首地址
// a+i == &a[i] : 代表第i行首地址
// *(a+i) == a[i] : 代表第i行首元素地址
// *(a+i)+j == &a[i][j] :代表第i行第j元素地址
// *(*(a+i)+j) == a[i][j] : 代表第i行第j元素的值
数组类型
typedef用于定义数组类型
有typedef是数组类型,没有则是变量
typedef int A[8]; //代表数组类型,不是变量
A a;
for(int i=0;i<8;i++) {
a[i] = i+10;
}
for(int i=0;i<8;i++) {
//printf("%d ", a[i]);
printf("%d ", *(a+i)); //等同
}
数组指针
是一个指针,指向一个数组的指针
- 先定义数组类型,根据类型定义指针变量
int a[10] = {1,2,3,4,5,6,7,8,9,10};
typedef int A[10]; //A数据类型 [10]代表步长
A *p = NULL; //p数组指针类型变量
p = &a; //&a代表整个数组首地址, p和a的长度要保持一致
//p = a; //a代表首元素地址, a 和 &a 一样,最终也是当作&a,有警告
printf("p:%d, p+1:%d", p, p+1); //p+1比p大40个字节,4*10
int a[][10] = {1,2,3,4,5,6,7,8,9,10, 11,12,13,14,15,16,17,18,19,20};
typedef int A[10]; //A数据类型 [10]代表步长
A *p = NULL; //p数组指针类型变量
//p = &a; //&a代表整个数组首地址, p和a的长度要保持一致,有警告,步长是4 * 2 * 10
p = a; //a代表首行首元素地址,步长是 4* 10
printf("p:%d, p+1:%d", p, p+1); //p+1比p大40个字节,4*10
也就是说,数组指针应该指向数组首行首地址,(不是首行首元素地址,也不是整个数组的首地址,本质上也就是步长的区别)
首行首地址步长:一行的长度 * 类型
首行首元素地址步长:单个元素 * 类型 = 1 * 类型
数组首地址 = 整个数组长度 * 类型
如果是一维数组,则数组首地址 = 首行首地址步长,因为只有一行,用&a表示
如果是二维数组,则a代表首行首地址, &a代表整个数组首地址
- 先定义数组指针类型,根据类型定义变量
int a[10] = {1,2,3,4,5,6,7,8,9,10};
typedef int (*P)[10];
P p;
p = &a;
for(int i=0; i<10;i++) {
// a[i]
// p = &a;
// *p = *&a -> a;
// (*p)[i] = a[i];
(*p)[i] = i + 1;
}
- 直接定义数组指针变量
指向数组的指针,没有typedef,所以是个数组指针
int(*p)[10]; //p数组指针变量
for(int i=0; i<10;i++) {
(*p)[i] = i + 1;
}













网友评论