天天看點

C 提高2 間接指派(*p) 是指針存在的最大意義

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);
	}

}
           
C 提高2 間接指派(*p) 是指針存在的最大意義

NULL 位址寫入資料

不可預料的位址寫入資料

不斷改變指針的指向:

C 提高2 間接指派(*p) 是指針存在的最大意義
C 提高2 間接指派(*p) 是指針存在的最大意義
C 提高2 間接指派(*p) 是指針存在的最大意義
#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  請按任意鍵繼續. . .

 */
           

一級指針 記憶體模型的建立

C 提高2 間接指派(*p) 是指針存在的最大意義
 #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;

	//見圖
}

/*
 編譯運作:

 */
           
C 提高2 間接指派(*p) 是指針存在的最大意義

字元串做函數參數

#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種方法

C 提高2 間接指派(*p) 是指針存在的最大意義
#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;
}

/*
 編譯運作:

 */
           

練習題:

C 提高2 間接指派(*p) 是指針存在的最大意義
C 提高2 間接指派(*p) 是指針存在的最大意義

第一題,我的答案:

#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