1野指針強化:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 野指針産生的原因
//指針變量和它指向的記憶體空間變量是兩個不同的概念
//釋放了指針所指向的記憶體空間,但是指針變量本省沒有重置為NULL
//造成釋放的時候,通過if(p1 != NUll)
//避免方法:
// 1)定義指針的時候,初始化成NULL
// 2)釋放指針所指向的記憶體空間,把指針重置成NULL
int main()
{
char *p1 = NULL;
p1 = (char *)malloc(100);
if (p1 == NULL)
{
printf("沒有配置設定到堆空間 \n");
return 0;
}
strcpy(p1, "1234567890");
printf("%s \n", p1);
if (p1 != NULL)
{
free(p1);
//p1 = NULL;//如果沒有這個步驟,下面的再次釋放就導緻程式出錯(VS2013)
}
if (p1 != NULL)
{
free(p1);
}
}
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnLwYTONJ3Z1tUa2FTQBFEbKJFdoVXcyYGZx02bpt0dvwFMD9CXygzLcBDMN9CXyAzcml3dvwVbvNmLvR3YxUjLxM3Lc9CX6MHc0RHaiojIsJye.png)
NULL 位址寫入資料
不可預料的位址寫入資料
不斷改變指針的指向:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char buf[20] = { 'a', '1', 'b', '2', 'c', '3', 'd', '4', 'e', '5' };
char *p1 = NULL;
char *p2 = NULL;
p1 = &buf[0];
p1 = &buf[1];
p1 = &buf[2];
int i = 0;
for (i = 0; i < 10; i++)
{
p1 = &buf[i];
printf("%c ", *p1);
}
p2 = (char *)malloc(20);
strcpy(p2,"1234567890");
for (i = 0; i < 10; i++)//不斷的修改指針的值,相當于不斷改變指針的指向
{
p1 = p2+i;//注意p2的步長是char
printf("%c ",*p1);
}
free(p2);
system("pause");
}
編譯運作:
C:\Users\chunli>gcc main.c & a
a 1 b 2 c 3 d 4 e 5 1 2 3 4 5 6 7 8 9 0 請按任意鍵繼續. . .
字面量:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
//結論:字面量不能取位址,沒有放在棧區,也沒有放在堆區,因為沒法取其位址
//我覺得放在代碼區
int i = 0; //字面量 0 0不能取位址
for (i = 0; i < 10; i++) //字面量 10 10不能取位址
{
printf("Hello World! \n");
}
}
便于運作:
C:\Users\chunli>gcc main.c & a
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
從0級指針到1級指針:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int fun1()
{
int a = 20;
return a;
}
int fun2(int a)
{
a = 30;
}
int fun3(int *a)
{
*a = 40;
}
int main()
{
int a = 10;
int *p = NULL;
p = &a;
printf("%d \n",*p);
fun1();
printf("%d \n", *p);
fun2(a); //隻是把a的數值傳過來,用來初始化函數的值,除此之外,函數内與a沒有任何關系了
printf("%d \n", *p);
fun3(&a); //隻有把a的位址傳過來,才能對a進行修改
printf("%d \n", *p);
system("pause");
}
/*
編譯運作:
10
10
10
40
請按任意鍵繼續. . .
*/
從1級指針到2級指針:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void fun1(char **p2) //隻有這種方法才能修改一級指針的數值
{
*p2 = 0xff; //間接修改一級指針的值,而不是修改一級指針指向的記憶體塊
}
void fun2(char *p2) //這種方法永遠也不可能修改傳過來的一級指針的數值
{
//char *p2 就相當于是在這裡定義了一個p2指針變量,和外界的一級指針沒有任何關系
p2 = 0xf;
}
int main()
{
char *p1 = NULL;
char *p2 = NULL;
//直接修改p1的值
p1 = 0x22;
p2 = 0x55;
//間接是修改p1的值
p2 = &p1; //把p1的位址裝入p2
*p2 = 100; //把變量p1的值修改為100
printf("%d \n",p1); //把p1的數值列印出來
fun1(&p1); //間接修改一級指針的值,而不是修改一級指針指向的記憶體塊
printf("%d \n", p1); //把p1的數值列印出來
fun2(p1);
printf("%d \n", p1); //把p1的數值列印出來
system("pause");
}
/*
編譯運作:
100
255
255
請按任意鍵繼續. . .
*/
【重要概念】
間接指派(*p) 是指針存在的最大意義
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void fun1(char **myp1, int *mylen1, char **myp2, int *mylen2)
{
int ret = 0;
char *tmp1 = NULL;
char *tmp2 = NULL;
//間接指派
tmp1 = (char *)malloc(100);
strcpy(tmp1, "1234567890");
*mylen1 = strlen(tmp1);// 1級指針
*myp1 = tmp1; //2級指針
tmp2 = (char *)malloc(200);
strcpy(tmp2, "abcdefg");
*mylen2 = strlen(tmp2);// 1級指針
*myp2 = tmp2; //2級指針
}
int main()
{
/*
原來p1的值是NULL,通過函數調用,p1指向了一塊記憶體區域
*/
char *p1 = NULL;
char *p2 = NULL;
int len1 = 0;
int len2 = 0l;
int ret = 0;
fun1(&p1, &len1, &p2, &len2);
if (ret != 0)
{
printf("error :%d \n", ret);
}
printf("%s \n", p1);
printf("%s \n", p2);
if (p1 != NULL)
{
free(p1);
p1 = NULL;
}
if (p2 != NULL)
{
free(p2);
p2 = NULL;
}
system("pause");
return ret;
}
/*
編譯運作:
1234567890
abcdefg
請按任意鍵繼續. . .
*/
間接指派的成立條件:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void fun1(int *p)
{
*p = 50;
}
int main()
{
//條件1,定義了2個變量
int a = 0;
int *p = NULL;
//條件2,建立關聯
p = &a;
//條件3,*p間接修改
*p = 10;
printf("%d \n",a);
//一級指針與函數
fun1(&a); //把實參的位址傳給形參,建立關聯
printf("%d \n", a);
system("pause");
return 0;
}
/*
編譯運作:
10
請按任意鍵繼續. . .
*/
//間接指派的應用場景
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
間接指派成立的三個條件:
條件1:定義變量(實參),定義變量(形參)
條件2:建立關聯
條件3:形參去間接的修改了實參的值
*/
//間接指派的應用場景
// 條件1 2 3 寫在一個函數中
int main()
{
char from[100] = { 0 };
char to[100] = { 0 };
char *p1 = from;
char *p2 = to;
strcpy(from,"112233445566");
while (*p1 != '\0')
{
*p2++ = *p1++;
}
printf("%s \n",to);
system("pause");
return 0;
}
/*
編譯運作:
112233445566
請按任意鍵繼續. . .
*/
字元串與一級指針 專題 --- //字元數組的初始化
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//字元串與一級指針 專題 --- //字元數組的初始化
// 1 C語言中的字元串是以數字0結尾的
// 2 在C語言中沒有字元串類型,通過字元數組來模拟字元串
// 3 字元串的記憶體配置設定可以在堆上 棧上 常量區
int main()
{
char buf1[100] = { 'a', 'b', 'c', 'd' };//除前面4個外,其他全是0
printf("%d \n",buf1[99]);
//char buf2[2] = { 'a', 'b', 'c', 'd' };//大于初始化的記憶體個數,編譯器報錯
char buf3[] = { 'a', 'b', 'c', 'd' };//這不是以0結尾的字元串
char buf4[] = "abcd"; //數組的大小是5,字元長度是4
int size = sizeof(buf4);
int len = strlen(buf4);
printf("size=%d, len=%d \n",size,len);
system("pause");
return 0;
}
/*
編譯運作:
0
size=5, len=4
請按任意鍵繼續. . .
*/
字元串與一級指針 專題 -- 用數組和指針操作字元串
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//字元串與一級指針 專題 -- 用數組和指針操作字元串
//普通指針與數組變量名字的差別
int main()
{
int i = 0;
char *p1 = NULL;
char buf1[128] = "abcdefg";
for (i = 0; i < strlen(buf1);i++)
{
printf("%c ",buf1[i]);
}
//用指針操作字元串
p1 = buf1;//buf代表數組元素的位址,這句話是錯誤的。應該說,buf代表數組首元素的位址
for (i = 0; i < strlen(buf1); i++)
{
printf("%c ", *(p1 + i)); //效果一樣
printf("%c ", *(buf1 + i)); //效果一樣 相當于buf1[i]
//[] 到* 的推導過程
//buf1[i] => buf1[0+i] => *(buf+i) ,buf代表數組首元素的位址
//其實[] 與 * 操作數組,沒有多大的差別,隻是中括号便于人們的閱讀
}
//不允許的操作
//buf1 = buf1 + 1; 左操作數必須為左值
system("pause");
return 0;
}
// 中括号[]的本質:和*是一樣,隻不過是符合程式員的閱讀習慣
// buf是一個指針,隻讀的常量,buf是一個常量指針。
//為什麼這麼做?
//buf是一個常量指針,析構記憶體的時候,保證buf空間完整釋放
/*
編譯運作:
a b c d e f g a a b b c c d d e e f f g g 請按任意鍵繼續. . .
*/
一級指針 記憶體模型的建立
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//一級指針 記憶體模型的建立
int main()
{
char buf1[20] = "aaa";
char buf2[] = "bbbb";
char *p1 = "11111111111111";
char *p2 = malloc(100);
strcpy(p2,"333333");
system("pause");
return 0;
//見圖
}
/*
編譯運作:
*/
字元串做函數參數
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//字元串做函數參數
int main()
{
char a[] = "i am a student";
char b[64];
int i = 0;
for (i = 0; *(a + i) != '\0'; i++)
{
*(b + i) = *(a + i); //'\0' 并不會存入
}
b[i] = '\0'; //'\0' 并不會存入
printf("%s \n",b);
system("pause");
return 0;
}
/*
編譯運作:
i am a student
請按任意鍵繼續. . .
*/
字元串拷貝,3種方法
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//字元串拷貝,3種方法
void copy_str1(char *from, char *to)
{
while (*from != '\0') //當遇到0就跳出來了
{
//指針的指向不斷變化,小心啊
// ++ 優先級高
*to++ = *from++; //'\0' 并不會存入
//相當于先 *to = *from, 在to++ from++
}
*to = '\0';
return;
}
void copy_str2(char *from, char *to) //優化一下
{
while ((*to = *from) != '\0') //先把值賦過去,再判斷
{
*to++;
*from++;
}
}
//經典程式之一 字元串拷貝
void copy_str(char *from, char *to) //再優化一下
{
while (*to++ = *from++); //先把值賦過去,再判斷
}
int main()
{
char *from = "Hello";
char buf[100] = {0};
copy_str(from,buf);
printf("%s \n",buf);
system("pause");
return 0;
}
/*
編譯運作:
i am a student
請按任意鍵繼續. . .
*/
經典程式之一 字元串拷貝
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//字元串拷貝,3種方法
//經典程式之一 字元串拷貝
//不要輕易改變形參的值
int copy_str(char *from, char *to) //再優化一下
{
char * tmp_to = to; //引進一個輔助
if (from == NULL || to == NULL) return -1;
while (*to++ = *from++); //先把值賦過去,再判斷
//不能出現 printf("%s \n",to)這樣的語句,因為to已經改變了
printf("%s \n",tmp_to);
return 0;
}
int main()
{
int ret = 0;
char *from = "Hello";
//char *buf = NULL;
char buf[20] ;
ret = copy_str(from,buf);
if (ret != 0)
{
printf("fun copy_str error %d \n", ret);
}
printf("%s \n",buf);
system("pause");
return ret;
}
/*
編譯運作:
i am a student
請按任意鍵繼續. . .
*/
項目開發字元串模型 do while 模型
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//項目開發字元串模型 do while 模型
int main()
{
int count = 0;
char *p = "11abcd123abcd234abcd23abcd2345abcd";
do
{
p = strstr(p, "abcd");
if (p != NULL)
{
count++;
p = p + strlen("abcd");
}
else
{
break;
}
} while (*p != '\0');
printf("%d \n" ,count);
system("pause");
return 0;
}
/*
編譯運作:
*/
項目開發字元串模型 while 模型
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//項目開發字元串模型 while 模型
int main()
{
int count = 0;
char *p = "11abcd123abcd234abcd23abcd2345abcd";
while (p=strstr(p,"abcd"))
{
count++;
p = p + strlen("abcd");
if (*p == '\0')
{
break;
}
}
printf("%d \n" ,count);
system("pause");
return 0;
}
/*
編譯運作:
*/
項目開發字元串模型 while 模型 -- API封裝
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//項目開發字元串模型 while 模型 -- API封裝
int getCount(char *String/*in*/, char *sub/*i*/, int *count/* out*/)
{
int tmp_count = 0;
int ret = 0;
char *p = String;
if (String == NULL || sub == NULL)
{
ret = -1;
printf("fun getCount error String == NULL || sub == NULL %d \n ",ret);
return ret;
}
while (p = strstr(p, sub))
{
tmp_count++;
p = p + strlen(sub);
if (*p == '\0')
{
break;
}
}
*count = tmp_count;
return 0;
}
int main()
{
int ret = 0;
int count = 0;
char *p1 = "11abcd123abcd234abcd23abcd2345abcd";
char *p2 = "abcd";
ret = getCount(p1,p2,&count);
if (ret != 0)
{
printf("fun fetCount error %d \n",ret);
}
printf("%d \n" ,count);
system("pause");
return ret;
}
/*
編譯運作:
*/
練習題:
第一題,我的答案:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int str_del(const char *str1/*in*/, char * str2/*out*/)
{
int ret = 0;
if (str1 == NULL || str2 == NULL)
{
ret = -1;
printf("fun str_del str1 == NULL || str2 == NULL error: %d \n",ret);
return ret;
}
char *p = str1;
while (*p == ' ')
{
p++;//跳出空格
}
strcpy(str2, p);//從第一個非空格開始,寫入原字元串
int len = strlen(str2);
if (str2[len-1] == ' ')//如果最後一位有空格
{
p = str2+len-1;//找到最後一個空格的位置
while (*p == ' ')
{
*p = 0;
p--;
}
}
return ret;
}
int main()
{
int ret = 0;
char *str1 =" abc 123 ";
char str2[128];
ret = str_del(str1,str2);
if (ret != 0)
{
printf("error in fun str_del code:%d\n",ret);
return ret;
}
printf("%s", str2);
system("pause");
return ret;
}
/*
編譯運作:
abc 123請按任意鍵繼續. . .
*/
第二題,我的答案:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int getstr(const char *str1/*in*/, char * str2/*out*/, char * str3/*out*/)
{
int ret = 0;
if (str1 == NULL || str2 == NULL || str3 == NULL)
{
ret = -1;
printf("fun str_del str1 == NULL || str2 == NULL || str3 == NULL error: %d \n",ret);
return ret;
}
char *p1 = str1;
char *p2 = str2;
char *p3 = str3;
int i = 0;
while (*p1 != '\0')
{
if (i % 2)
*p2++ = *p1;
else
*p3++ = *p1;
i++;
p1++;
}
return ret;
}
int main()
{
int ret = 0;
char *string ="1a2b3c4da1b2c3d4";
char str1[128] = { 0 };
char str2[128] = { 0 };
ret = getstr(string,str1,str2);
if (ret != 0)
{
printf("error in fun str_del code:%d\n",ret);
return ret;
}
printf("str1=%s\n", str1);
printf("str2=%s\n", str2);
system("pause");
return ret;
}
/*
編譯運作:
str1=abcd1234
str2=1234abcd
請按任意鍵繼續. . .
*/
指針經典話語:
1,指針指向誰,就把誰的位址賦給指針;
2,指針變量 和 它指向的記憶體空間變量是兩個不同的概念
3,了解指針的關鍵是記憶體,沒有記憶體哪裡來的指針
變量的本質是一個固定大小的資料塊,變量名就是資料塊的編号
記憶體的使用範圍:
main函數可以在棧配置設定記憶體/堆配置設定記憶體/全局配置設定記憶體,可以給子函數使用
子函數在棧配置設定的記憶體不能給主函數使用,但是堆記憶體與全局變量是可以給main使用
編譯器會為每個程式配置設定一個記憶體4區,主函數與子函數公用這個記憶體4區
建立正确程式運作記憶體布局圖是學好C的關鍵!
指針鐵律1:指針是一種資料類型
1)指針也是一種變量,占有記憶體空間,用來儲存記憶體位址
2)*p 操作記憶體;
3)*就像一把鑰匙,通過一個位址(&a),去修改a變量的标示的記憶體空間
4)不斷的給指針指派,相當于不停的改變指針的指向。
5) 指針是一種資料類型,是指它指向記憶體空間的資料類型
指針鐵律1:間接指派(*p) 是指針存在的最大意義
轉載于:https://blog.51cto.com/990487026/1789245