第十八章-C语言进阶(一)
指针强化
- 指针也是一种变量,占有内存空间,用来保存内存地址
- 即指针也是一种变量,分配内存空间,内存空间的大小由编译器决定,比如:VS编译器下指针是4个字节
- 同一种编译器下,所有的指针类型占用的内存空间都一样,跟指针的类型无关
- 即:
int *p;doub *p; char *p,char **p;
这4个p占用内存大小都是4个字节(vs编译器而言)
- 即:
- 那么指针的数据类型有什么用呢?
- 指针的数据类型是指这个指针指向的内存中存放的数据是什么类型。
- 指针步长(p++),根据所指内存存储的数据类型来确定,每次++,加几个字节
- 指针变量和他指向的内存块
- 修改指针的值,只会改变指针的指向,并不会改变指针所指内存块中的数据;p = 0x1111;
- *p = a;修改指针指向内存中存储的值,并不会改变指针变量的值。
- *p操作内存
- 在指针声明时,*号表示所声明的变量为指针
- 在指针使用时,*号表示 操作 指针所指向的内存空间中的值
- *p相当于通过地址(p变量的值)找到一块内存;然后操作内存
- *p放在等号的左边赋值(给内存赋值)
- *p放在等号的右边取值(从内存获取值)
-
字符串一级指针内存模型
char buf[20]= "aaaa"; char buf2[] = "bbbb"; char *p1 = "111111"; char *p2 = malloc(100); strcpy(p2, "3333");
- 常量区中分配存放4个字符串:
“aaaa”、“bbbb”、“111111”、“3333”
- 栈区:
- 先给buf分配20个字节的内存,内存中将常量“aaaa”拷贝进去
- 再给buf2分配5个字节的内存,内存中将常量“bbbb”拷贝进去
- 给指针p1分配4个字节,用于存储常量区“111111”的存储地址
- 给指针p2分配4个字节,用于存储堆区100个字节内存的首地址
- 堆区
- 通过malloc在堆区分配100个字节的内存
- 通过strcpy,将“3333”常量拷贝到堆区中。
- 常量区中分配存放4个字符串:
通过递归的方式逆向排序
-
正常的逆序(采用两头排序)
//逆序 int inverse(char *str1){ int length; char *p1; char *p2; if (str1 == NULL) return -1; length = strlen(str1); p1 = str1; //两头堵:从2头开始比较 p2 = str1 + length - 1; while (p1<p2) { char c = *p1; *p1 = *p2; *p2 = c; ++p1; --p2; } }
-
递归和全局变量:把逆序的结果存入全局变量
//采用全局变量来接收递归的结果 char g_buf[1000]; void inverse1(char *p){ //递归结束的异常条件 if (p == NULL) return; //递归结束条件 if (*p == '\0') return; //注意此时没有执行打印,而是执行了函数调用,本质是让p+1所指向的地址入栈,即让字符串的而每一个地址入栈。 inverse1(p+1); //printf("%c",*p); //让字符串的每一个地址入栈 //这个函数会将p指针指向的内容拷贝g_buf,但是每次使用一次会覆盖上一次 //strncpy(g_buf, p, 1); //这个函数会将p指针指向的内容拷贝g_buf,而且每次使用一次是拼接上一次拷贝的后面 strncat(g_buf, p, 1); }
-
递归和非全局变量:递归指针做函数参数
void inverse2(char *p,char *bufresult){ if (p == NULL) return; if (*p == '\0') return; inverse2(p + 1,bufresult); strncat(bufresult, p, 1); }
-
主函数调用
int main(){ char buf[] = "abcdefg"; //最常用方法 { int length = strlen(buf); inverse(buf); printf("buf:%s\n", buf); } //采用递归方法逆序 //采用全局变量来接收逆序结果 { memset(g_buf, 0, sizeof(g_buf)); inverse1(buf); printf("g_buf:%s\n", g_buf); } //采用局部变量来接收递归的结果 { char mybuf[1024] = { 0 }; inverse2(buf, mybuf); printf("mybuf:%s\n", mybuf); } getchar(); return 0; }
一级指针易错模型分析
- 不断修改指针变量的值
-
释放修改后的堆指针
//释放的内存不对 char *getKeyByValue(char **keyvaluebuf, char *keybuf){ int i = 0; char *a = (char *)malloc(50); printf("%x", a); for (; **keyvaluebuf != '\0'; i++) { *a++ = *(*keyvaluebuf)++; } //此时的a,并不是指向开始分配的堆的首地址了,所以释放不对 free(a); return; }
-
仍然使用修改后的指针打印原值
void copy_str_err(char *from, char *to) { for (; *from != '\0'; from++, to++) { *to = *from; } *to = '\0'; //此时的to,跟from已经被++改变了,因此肯定打印不出来原来的字符串!!! printf("to:%s", to); printf("from:%s", from); }
-
-
局部变量的作用域
//功能:将x,y字符串,存入到一个数组中 char *str_cnct(char *x, char* y) /*简化算法*/ { char str3[80]; char *z = str3; /*指针z指向数组str3*/ //存x while (*z++ = *x++); z--; /*去掉串尾结束标志\0*/ //存y while (*z++ = *y++); z = str3; /*将str3地址赋给指针变量z*/ //错误:由于str3是局部变量,当该函数运行完毕后,str3将会被释放,指向的内存空间的数据也会被释放。 return(z); }
const 总结
const int a; //
int const b;
const char *c;
char * const d;
const char * const e ;
- 第一个第二个意思一样 代表一个常整形数
- 第三个 c是一个指向常整形数的指针(所指向的内存数据不能被修改,但是本身可以修改)
- 第四个 d 常指针(指针变量不能被修改,但是它所指向内存空间可以被修改)
- 第五个 e一个指向常整形的常指针(指针和它所指向的内存空间,均不能被修改)
二级指针
- 二级指针做输入和输出模型
- 指针做输入:主调函数内部分配内存
- 指针做输出:被调函数内部分配内存。
//指针做输出:背调函数分配内存 //求文件中两段话的长度 int getMem(char **myp1, int *mylen1, char **myp2, int *mylen2) { char *tmp1 = NULL; char *tmp2 = NULL; tmp1 = (char *)malloc(100); if (tmp1 == NULL) { return -1; } strcpy(tmp1, "abcdefg"); *mylen1 = strlen(tmp1); *myp1 = tmp1; //间接修改实参p1的值 tmp2 = (char *)malloc(100); if (tmp2 == NULL) { return -2; } strcpy(tmp2, "11122233333"); *mylen2 = strlen(tmp2); *myp2 = tmp2; //间接修改实参p2的值 return 0; } int getMem_Free(char **myp1) { char *tmp = NULL; if (myp1 == NULL) { return -1; } tmp = *myp1; free(tmp); // 释放完指针变量所指的内存空间 *myp1 = NULL; //把实参修改成NULL return 0; } void main() { char *p1 = NULL; int len1 = 0; char *p2 = NULL; int len2 = 0; int ret = 0; //中指:如果想要在被调函数中修改主调函数的数据,那就需要传给背调函数地址 //因此,如果被调函数原来是整型,那么就是取地址,一级指针;如果被调函数原来是指针,那么取地址,就是二级指针!! ret = getMem(&p1, &len1, &p2, &len2 ); printf("p1: %s \n", p1); printf("p2: %s \n", p2); getMem_Free(&p1); getMem_Free(&p2); system("pause"); return ; }