(1) 实参的a和形参的a的数据类型本质不一样,形参中的数组编译器会把它当成指针处理, 调用函数时需将数组名(内存首地址)和数组的有效长度传给被调用函数。
(2) 数据类型的本质:固定大小内存块的别名 b+1、&b+1 结果不一样:b、&b所代表的数据类型不一样 b代表的数组首元素的地址 &b代表的是整个数组的地址
(3) 一般认为:栈开口向下,不管栈开口向上还是向下,buf的内存地址buf+1永远向上的
(4) 野指针产生的原因:指针变量和它所指向的内存空间变量是两个不同的概念 释放了指针所致的内存空间,但是指针变量本身没有重置成null,造成释放的时候通过if (p1 != NULL) 避免方法:
- 定义指针的时候,初始化成NULL
- 释放指针所指向的内存空间后,把指针重置成NULL
(5) 数组 char buf[10] = “abcd”; 指针 char *p = buf; C语言的字符串是以零结尾的字符串,在C语言中没有字符串类型,通过字符数组来模拟字符串 字符串的内存分配:堆上、栈上、全局区
char buf[10] = "abcd";
strlen(buf) 长度不包括\0 =4(字符串)
sizeof(buf) 内存块的大小 =5(字符数组)
char buf1[10] = { 'a', 'b', 'c', 'd' };
char buf2[] = { "abcd" };
cout << strlen(buf1) << endl;
cout << strlen(buf2) << endl;
cout << sizeof(buf1) << endl;
cout << sizeof(buf2) << endl;
buf是一个常量指针(只读的常量) buf++ //error p++ //ok
(6) 一个由C/C++编译的程序占用的内存分为以下几个部分:
- 栈区(stack):由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其
操作方式类似于数据结构中的栈; - 堆区(heap):一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回
收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表; - 全局区(静态区)(static):全局变量和静态变量的存储是放在一块的,初始化的
全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另 一块区域,程序结束后由系统释放。 - 文字常量区(只读,不允许修改):常量字符串就是放在这里的。 程序结束后由系统释放 ;
- 程序代码区 — 存放函数体的二进制代码;
(7) char *p = “abcd”; char a[] = “abcd”;
指针p和字符数组都是在栈区,其中"abcd"字面值在常量区,上面的区别是字符数组a会把常量区的"abcd"拷贝到栈中。 那么字符数组a可以修改其中的元素,但指向字符的指针不能修改其指向的字符"abcd"。
char *p = "abcd";
*(p + 2) = '2';
如果真的需要使用"abcd"作为指针,建议写为const char * p=“abcd”; 如果是初始化字符串数组,建议写为char p[]=“abcd”;
char *p;
p=malloc(STR_SIZE);
strcpy(p,"abcd");
(8)
const int a;
int const b;
const char *c;
char * const d;
const char * const e;
指针变量和它所指向的内存空间变量,是两个不同的概念 看const 是放在 * 的左边还是右边 看const是修饰指针变量,还是修饰所指向的内存空间变量 (9)
指针做输入:主调用函数分配内存 指针做输出:被调用函数分配内存
主调函数 被调函数 (1)主调函数可把堆区、栈区、全局数据内存地址传给被调用函数 (2)被调用函数只能返回堆区、全局数据
char *getMem(int num)
{
char *p1 = NULL;
p1 = (char *)malloc(sizeof(char) * num);
if (p1 == NULL)
{
return NULL;
}
return p1;
}
char *getMem2()
{
char buf[64];
strcpy(buf, "123456789");
return buf;
}
void main()
{
char *tmp = NULL;
tmp = getMem(10);
if (tmp == NULL)
{
return ;
}
strcpy(tmp, "111222");
tmp = 0xaa11;
return;
}
(10) 指针数组和数组指针(核心:运算符的优先级) 指针数组:首先它是一个数组,数组的元素都是指针,数组占多少个字节由数组本身决定。它是“储存指针的数组”。 数组指针:首先它是一个指针,它指向一个数组。在32 位系统下永远是占4个字节,至于它指向的数组占多少字节,不知道。它是“指向数组的指针”。
(1)int *p1[10];
[]的优先级比“*”要高。p1先与“[]”结合,构成一个数组的定义,数组名为p1,int *修饰的是数组的内容,即数组的每个元素。
即这是一个数组,其包含10个指向int类型数据的指针,即指针数组。
int* a[4];
表示:数组a中的元素都为int型指针
元素表示:*a[i] *(a[i])是一样的,因为[]优先级高于*
typedef int* pInt;
pInt a[4];
(2)int (*p2)[10];
在这里“()”的优先级比“[]”高,“*”号和p2 构成一个指针的定义,指针变量名为p2,int修饰的是数组的内容,即数组的每个元素。
数组在这里并没有名字,是个匿名数组。即p2 是一个指针,它指向一个包含10个int 类型数据的数组,即数组指针。
一维数组 char a[30] 指针 char*
指针数组 char *a[30] 指针的指针 char **a
二维数组 char a[10][30] 数组指针 char(*a)[30]
(3)
char a[5]={'A','B','C','D'};
char (*p3)[5] = &a;
char (*p4)[5] = a;
p3 和p4 都是数组指针,指向的是整个数组。&a 是整个数组的首地址,a是数组首元素的首地址,其值相同但意义不同。
在C语言里,赋值符号“=”号两边的数据类型必须是相同的,如果不同需要显示或隐式的类型转换。
p3 这个定义的“=”号两边的数据类型完全一致。
而p4 这个定义的“=”号两边的数据类型就不一致了,左边的类型是指向整个数组的指针,右边的数据类型是指向单个字符的指针。
cout << "p3+1 = " << p3 + 1 << endl;
cout << "&a[1] = " << &a+1 << endl;
(11) 二级指针三种内存模型示意图 (12)
指针也是一种变量,占有内存空间,用来保存内存地址 指针是一种数据类型,是指它指向的内存空间的数据类型(指针变量和它指向的内存块是两个不同的概念) 结论:指针的步长,根据所指内存空间类型来定
(13)
两码事:指针变量和它指向的内存块变量 条件反射:指针指向某个变量,就是把某个变量地址赋给指针
*p间接赋值成立条件:3个条件
- 2个变量(通常一个实参,一个形参)
- 建立关系,实参取地址赋给形参指针
- *p形参去间接修改实参的值
int iNum = 0;
int *p = NULL;
p = &iNum;
iNum = 1;
*p =2 ;
引申: 函数调用时,用n指针(形参)改变n-1指针(实参)的值。 函数调用时,用实参取地址,传给形参,在被调用函数里面用*p,来改变实参,把运算结果传出来,此为指针作为函数参数的精髓。
|