學習過程中部分c語言疑惑問題的程式碼驗證

2020-08-13 12:24:12

所有程式碼均爲曾經學習過程中不清楚不或明白的問題,做的驗證,拿出來分享,文章有點長哦
內容涉及(按文章中的先後順序):argc和argv;const;getmemory;各種數據型別所佔用記憶體大小;pragma和error;sizeof(*p)和sizeof§;sizeof和strlen;void指針;volatile;while(–a)和while(a–);單鏈表;雙鏈表;猴子吃桃;階乘;矩陣乘法;型別轉換;冒泡選擇排序;大小端;陣列首地址表示方法;陣列作爲結構體變數;陣列作爲結構體成員;陣列作爲形參;野指針;指針存取二維陣列;指針分配動態記憶體;指針函數與函數指針;指針陣列;質數、最小公倍數、最大公約數、閏年;字串逆序輸出;字串常數與字元陣列;

1.argc和argv

//#include "stdafx.h"
//#include "iostream"

#include<stdio.h>
//using namespace std;

int main(int argc, char* argv[])
{
	int i;

	for (i = 0; i < argc; i++)
	{
		//cout << "argument" << i << ": " << argv[i] << endl;
		printf("argv[%d] = %s\n",i,argv[i]);
	}
	//cout << "total argument:" << argc;
	printf("argc = %d\n", argc);

		//return EXIT_SUCCESS;
		return 0;
}
*/

/*
兩個參數中第一個參數argc表示在Dos命令列中的輸入的程式名和參數個數之和,第二個參數中argv[0]記錄的程式名,後面的argv[i]記錄的輸入參數。
另外argc argv是識別符號,可以修改名稱。
*/

2.const

/*
#include<stdio.h>
int main(void)
{
	const int a = 12;
	const int* p = &a;
	p++;
 &b;
	t++;
	p = &b;
	q = &b;	p--;
	int const* q = &a;
	int b = 12;
	int* const r = &b;
	r++;
	const int* const t =
	return 0;
}
*/
/*

#include<stdio.h>
int main()
{ const int a = 12;
const int* p = &a; //這個是指向常數的指針, 指針指向
一個常數
p++; //指針可以自加、 自減
p--; //合法
int const* q = &a; //這個和上面的「const int*p=&
a; 」是一個意思
int b = 12;
int* const r = &b; //這個就是常數指針(常指針) , 不
能自加、 自減, 並且要初始化
//r++; //編譯出錯
const int* const t = &b; //這個就是指向常數的常指
針, 並且要初始化, 用變數初始化
//t++; //編譯出錯
p = &b; //const指針可以指向const和非const物件
q = &b; //合法
return 0;
}
*/
/*
#include<stdio.h>
int main(void)
{
	int a = 12;
	int b = 13;
//第一種
	//const int* p = &a;	//或者int const*p=&a;		指向常數的指針,指針的地址可以改,但不能對指針內的數據進行改動,因爲這時* p  是一個常數,但	指針p	可以改動
	//可以在後面這麼寫,(1)	p = &b;	(2)	a = 13;	(3)p++;這三種是合法的	不合法的一種爲(1)*p=13;
//第二種
	int* const p = &a;		//常數指針,指針p爲常數,此時指針p爲常數,所指向的地址不能改動,但指針所指地址數據可以改動
	//可以在後面這樣寫,(1)a=13;	(2)*p=13;這兩種都是合法的   不合法的兩種爲(1)p = &b;	(2)p++;
//第三種
	//const int* const p= &a;		//指向常數的常指針,並且要初始化,用變數初始化,*p   指針p都是隻讀的,不可改動
	//可以在後面這樣寫,(1)a=13;這種是合法的,不合法的三種爲(1)p = &b;	(2)*p = 13;	(3)p++;
	//a = 13;
	//p = &b;
	//*p = 13;
	//p++;
	printf("%d\n", *p);
}
*/

3.getmemory

//#pragma warning( disable : 4996)

/*
void getmemory(char *p)
   {
  p=(char *) malloc(100);
  strcpy(p,"hello world");
   }
   int main( )
   {
  char *str=NULL;
  getmemory(str);
  printf("%s/n",str);
  free(str);
  return 0;
	}
 程式崩潰,getmemory中的malloc 不能返回動態記憶體, free()對str操作很危險
77、void GetMemory(char *p)
 {
  p = (char *)malloc(100);
 }
 void Test(void)
 {
  char *str = NULL;
  GetMemory(str);
  strcpy(str, "hello world");
  printf(str);
 }

 請問執行Test函數會有什麼樣的結果?
 答:程式崩潰。因爲GetMemory並不能傳遞動態記憶體,Test函數中的 str一直都是 NULL。strcpy(str, "hello world");將使程式崩潰。

 void GetMemory2(char **p, int num)
 {
  *p = (char *)malloc(num);
 }
 void Test(void)
 {
 char *str = NULL;
 GetMemory(&str, 100);
 strcpy(str, "hello");
 printf(str);
 }
 請問執行Test函數會有什麼樣的結果?
 答:(1)能夠輸出hello
	 (2)記憶體漏失,因爲忘記free
78、char *GetMemory(void)
 {
  char p[] = "hello world";
  return p;
 }
 void Test(void)
 {
  char *str = NULL;
  str = GetMemory();
  printf(str);
 }
 請問執行Test函數會有什麼樣的結果?
 答:可能是亂碼。因爲GetMemory返回的是指向「棧記憶體」的指針,該指針的地址不是 NULL,
 但其原先的內容已經被清除,新內容不可知。
 ---------------------------------------
  下面 下麪程式執行有什麼樣的結果?

	char *GetString(void)

	{

	  char array[6];

	  strcpy(array, 「hello」);

	  return array;

	}

	void main()

	{

	  char *pstr = NULL;

	  pstr = GetString();

	  printf("%s\n", pstr);

	}

	答對這個問題,腦子裏必須有一根弦,那就是棧記憶體裡的內容如「曇花一現」,其有效的生命週期只等於函數週期,存取棧記憶體的前提條件是在函數執行範圍內,函數一旦退出,相應的棧記憶體立馬消散,如同鏡花水月,再存取就是野指針的「非法」操作。

	上例中,函數char *GetString返回了指向區域性陣列array[6]的指針,但array[6]位於棧中,函數的棧記憶體在其結束後立刻失效,函數退出後再試圖通過pstr存取棧記憶體就成了非法操作。因此,返回指向棧記憶體的指針這種機制 機製,看似在函數退出後繼續存取棧記憶體留了一個「後門」,實際上是一個隱晦的陷阱。再比較下面 下麪例子:
	*/
/*
#define _CRT_SECURE_NO_WARNINGS
#pragma warning( disable : 4996)
#include<stdio.h>
#include<string.h>
#include<malloc.h>


	char *GetString(void)

   {

	  char array[6]="hello";

	  char *p = (char *)malloc(6);

	  strcpy(p, array);

	  return p;

	}

	int main(void)

	{

	  char *str = NULL;

	  str = GetString();

	  printf("%s\n", str);

	  free(str);

	  return 0;
	  

	}
	
	
	這裏先把hello字串從array[]所在的棧記憶體拷貝到指針p指向的堆記憶體中,當GetString 函數結束後,array[]棧記憶體失效了,但指針p所指的堆記憶體仍有效並儲存着」hello」串,函數返回指向堆的指針就沒有問題了。如果把直接用指針指向字串呢:

	char *GetString(void)

	{

	  char *p = "hello";

	  return p;

	}

	void main()

	{

	  char *str = NULL;

	  str = GetString();

	  printf("%s\n", str);

	}

	把原先的陣列修改成指針,即 char *str="hello",這樣也可行,因爲"hello"串位於常數數據區,在子函數結束後依然有效,通過返回的p指針仍然可以讀取這塊記憶體。
	 棧本身主要是由編譯器靜態維護和管理,所以程式中一般情況下應該避免使用指向棧的指針
---------------------------------------------
79、void Test(void)
 {
   char *str = (char *) malloc(100);
  strcpy(str, 「hello」);
  free(str);
  if(str != NULL)
  {
	strcpy(str, 「world」);
    printf(str);
   }
 }
 請問執行Test函數會有什麼樣的結果?
 答:篡改動態記憶體區的內容,後果難以預料,非常危險。因爲free(str);之後,str成爲野指針,
 if(str != NULL)語句不起作用。
 野指針不是NULL指針,是指向被釋放的或者存取受限記憶體指針。
 造成原因:指針變數沒有被初始化任何剛建立的指針不會自動成爲NULL;
	 指針被free或delete之後,沒有置NULL;
	 指針操作超越了變數的作用範圍,比如要返回指向棧記憶體的指針或參照,因爲棧記憶體在函數結束時會被釋放。
*/

4各種數據型別所佔用記憶體大小

/*
#include<stdio.h>
#include<stdlib.h>

int main()
{
	int n = 0;														//整型
	char ch = 0;													//字元
	int arr1[5] = { 0 };											//整型陣列
	char arr2[5] = { 0 };											//字元陣列
	int* p1 = &n;													//整形指針
	char* p2 = &ch;													//字元指針
	int* p3 = arr1;													//整形指針
	char* p4 = arr2;												//字元指針
	int(*p5)[5] = &arr1;											//整形陣列指針
	char(*p6)[5] = &arr2;											//字元陣列指針
	int* arr3[5] = { p1 };											//整形指針陣列
	char* arr4[5] = { p2 };											//字元型指針陣列
	printf("int佔%d個位元組\n", sizeof(int));
	printf("short佔%d個位元組\n", sizeof(short));
	printf("char佔%d個位元組\n", sizeof(char));
	printf("long佔%d個位元組\n", sizeof(long));
	printf("float佔%d個位元組\n", sizeof(float));
	printf("long long佔%d個位元組\n", sizeof(long long));
	printf("double佔%d個位元組\n", sizeof(double));
	printf("整形變數佔%d個位元組\n", sizeof(n));
	printf("字元變數佔%d個位元組\n", sizeof(ch));
	printf("整形陣列 int [5] 佔%d個位元組\n", sizeof(arr1));
	printf("字元陣列 char [5] 佔%d個位元組\n", sizeof(arr2));
	printf("整形指針 int* 佔%d個位元組\n", sizeof(p1));
	printf("字元指針 char* 佔%d個位元組\n", sizeof(p2));
	printf("整形陣列指針 佔%d個位元組\n", sizeof(p5));
	printf("字元陣列指針 佔%d個位元組\n", sizeof(p6));
	printf("整形指針陣列 佔%d個位元組\n", sizeof(arr3));				//指針4個位元組,陣列5個位元組4*5=20
	printf("字元指針陣列 佔%d個位元組\n", sizeof(arr4));				//指針4個位元組,陣列5個位元組4*5=20
	printf("指向整形陣列的指針 佔%d個位元組\n", sizeof(p3));
	printf("指向字元陣列的指針 佔%d個位元組\n", sizeof(p4));



	//system("pause");
	return 0;
}
*/
/*
#include<stdio.h>
#include<stdlib.h>

int main()
{
	printf("int佔%d個位元組\n", sizeof(int));
	printf("short佔%d個位元組\n", sizeof(short));
	printf("char佔%d個位元組\n", sizeof(char));
	printf("long佔%d個位元組\n", sizeof(long));
	printf("float佔%d個位元組\n", sizeof(float));
	printf("double佔%d個位元組\n", sizeof(double));
	printf("long long佔%d個位元組\n", sizeof(long long));

	printf("\n");

	printf("unsigned char佔%d個位元組\n", sizeof(unsigned char));
	printf("unsigned int佔%d個位元組\n", sizeof(unsigned int));
	printf("unsigned long佔%d個位元組\n", sizeof(unsigned long));
	printf("unsigned short佔%d個位元組\n", sizeof(unsigned short));
	printf("unsigned float佔%d個位元組\n", sizeof(unsigned float));//"unsigned": 不能與型別 "float" 一起使用
	printf("unsigned double佔%d個位元組\n", sizeof(unsigned double));//	"unsigned": 不能與型別 "double" 一起使用	




	//system("pause");
	return 0;
}
*/
/*
1、unsigned的作用就是將數位型別無符號化, 例如 int 型的範圍:-2^31 ~ 2^31 - 1,而unsigned int的範圍:0 ~ 2^32。
   看起來unsigned 是個不錯的型別,尤其是用在自增或者沒有負數的情況。但是在實際使用中會出現一些意外的情況。

2、signed在預設情況下宣告的整型變數都是有符號的型別(char有點特別),如果需宣告無符號型別的話就需要在型別前加上unsigned。
   無符號版本和有符號版本的區別就是無符號型別能儲存2倍於有符號型別的正整數數據。


unsigned和signed的區別

1、所有比int型小的數據型別(包括char,signed char,unsigned char,short,signed short,unsigned short)轉換爲int型。
如果轉換後的數據會超出int型所能表示的範圍的話,則轉換爲unsigned int型

2、bool型轉化爲int型時,false轉化爲0,true轉換爲1;反過來所有的整數型別轉化爲bool時,0轉化爲false,其它非零值都轉爲true

3、如果表達式中混有unsigned short和int型時,如果int型數據可以表示所有的unsigned short型的話,則將unsigned short型別的數據轉換爲int型,
否則,unsigned short型別及int型都轉換爲unsigned int型別

舉個例子,在32位元機上int是32位元,範圍–2,147,483,648 to 2,147,483,647,unsigned short是16位元,範圍0 to 65,535,
這樣int型的足夠表示unsigned short型別的數據,因此在混有這兩者的運算中,unsigned short型別數據被轉換爲int型

4、unsigned int 與long型別的轉換規律同3,在32位元機上,unsigned int是32位元,範圍0 to 4,294,967,295,long是32位元,範圍–2,147,483,648 to 2,147,483,647,
可見long型別不夠表示所有的unsigned int型,因此在混有unsigned int及long的表達式中,兩者都被轉換爲unsigned long

5、如果表達式中既有int 又有unsigned int,則所有的int數據都被轉化爲unsigned int型別
*/

5.pragma和error

/*
#include<stdio.h>
#define STR
void main()
{
printf("學習#pragma命令中message參數的使用!\n");
#ifdef STR
#pragma message("STR已經定義過了")	//該指令用於在預處理過程中輸出一些有用的提示資訊
#endif
return;
}

可以利用#pragma pack來改變編譯器的對齊方式:
#pragma pack(n)  //指定n位元組對齊
#pragma pack()	 //取消對齊方式

#pragma warning//該指令允許選擇性地修改編譯器警告資訊。
#pragma warning( disable : 4507 34; once : 4385; error : 164 )
等價於
#pragma warning(disable:4507 34) // 不顯示4507和34號警告資訊
#pragma warning(once:4385)       // 4385號警告資訊僅報告一次
#pragma warning(error:164)       // 把164號警告資訊作爲一個錯

============================================================

#if(...)
	.....;
#elif
	.....;
#else
	#error(....) ;//指令讓前處理器發出一條錯誤資訊,並且會中斷編譯過程。

*/

6.return


/*
#include<stdio.h>
int test(int n);
int main()
{

	printf("%d\n", test(5));
	return 0;
}
int test(int n)
{

	if (3 == n)
		return 2;
	return 3;
}

*/

7.return和printf


//以求最小公倍和最大公約數的程式爲例

//printf
/*
#include<stdio.h>
int max_gongbei(int, int);
int main(void)
{
	int a, b;
	printf("請輸入兩個數:");
	scanf_s("%d%d", &a, &b);
	max_gongbei(a, b);
}
int max_gongbei(int a, int b)
{
	int c = a % b, m, n;
	m = a;
	n = b;
	while (0 != c)
	{
		a = b;
		b = c;
		c = a % b;
	}
	printf("%d和%d的最大公約數爲%d\n", m, n, b);
	printf("%d和%d的最小公倍數爲%d\n", m, n, (m * n / b));
	return 0;
}
*/


//return
/*
#include<stdio.h>
int max_gongbei(int, int);
int main(void)
{
	int a, b;
	printf("請輸入兩個數:");
	scanf_s("%d%d", &a, &b);
	//max_gongbei(a, b);
	printf("%d和%d的最大公約數爲%d\n", a, b, max_gongbei(a, b));
	printf("%d和%d的最大公倍數爲%d\n", a, b, a*b/max_gongbei(a, b));
}
int max_gongbei(int a, int b)
{
	int c = a % b, m, n;
	m = a;
	n = b;
	while (0 != c)
	{
		a = b;
		b = c;
		c = a % b;
	}
	return b;
}
*/

8.sizeof(*p)和sizeof§

/*
#include <stdio.h>

 int main(void)
 {
	//一維陣列
	char ca[] = "123456";
	char *pca = "123456";
	printf("sizeof(ca)=%d,sizeof(pca)=%d,sizeof(*pca)=%d\n",sizeof(ca),sizeof(pca),sizeof(*pca));
	//二維陣列
	char cb[3][7] = {"123456","123456","123456"};
	char *pcb[] = {"123456","123456","123456"};
	printf("sizeof(cb)=%d,sizeof(pcb)=%d,sizeof(*pcb)=%d,sizeof(**pcb)=%d\n",sizeof(cb),sizeof(pcb),sizeof(*pcb),sizeof(**pcb));
	return 0;
}
*/



 /*
 sizeof(陣列名)返回的是陣列的大小

sizeof(ca)=7,注意此處包含一個結束符
sizeof(cb)=18,

sizeof(一維陣列指針)返回的是陣列單個元素指針的大小,即系統指針的長度,32位元系統爲4,64位元系統位8

64位元:sizeof(pca)=8,32位元:sizeof(pca)=4

sizeof(*一維陣列指針)返回的是陣列單個元素對應型別的大小

sizeof(*pca)=1,char的大小

sizeof(二維陣列指針)返回的是二維陣列行指針的大小,32位元系統爲行數×4,64位元系統位行數×8

sizeof(pcb)等同於sizeof(pcb[0])、sizeof(pcb[1])、sizeof(pcb[2])

64位元:sizeof(pcb)=24,3行×8,注意二維陣列每一行的結尾沒有結束符'\0',所以是行長度爲8

32位元:sizeof(pcb)=12,3行×4,

sizeof(*二維陣列指針)返回的是系統指針的長度,32位元系統爲4,64位元系統位8

64位元:sizeof(pcb)=8,32位元:sizeof(pcb)=4

sizeof(**二維陣列指針)返回的是陣列單個元素指針的大小,即char的大小

sizeof(**pcb)=1,

 */

9.sizeof和strlen

/*
#include<stdio.h>
#include<string.h>

int main(void)
{
	char a[7] = "ILOVEC";
	//int a[7] = {1,2,3,4,5,6,7};
	printf("str[%d]\n", strlen(a));
	printf("siz[%d]\n", sizeof(a));
	printf("siz2[%d]\n", sizeof(a)/sizeof(a[0]));
	return 0;

}
*/

10.void指針

/*
#include<stdio.h>
int main(void)
{
	void* n;
	int a = 9;
	int* pa;
	pa = &a;
	n = &a;
	printf("pa=%d\t*pa=%d\t", pa, *pa);
	pa++;//只能做測試用,因爲這裏pa++後pa所指向的地址不知道是否可用,無意義
	//printf("%d", n++);		//不能n++;也不能int*n++;即void*型別的指針不能++
	printf("pa++=%d\t", pa);
	printf("*(int*)n=%d\t", *(int*)n);
}

*/

11.volatile

/* 
volatile關鍵字是一種型別修飾符,用它宣告的型別變數表示可以被某些編譯器未知的因素更改,比如

操作系統、硬體或者其它執行緒等。遇到這個關鍵字宣告的變數,編譯器對存取該變數的程式碼就不再進行

優化,從而可以提供對特殊地址的穩定存取。
*/



/*
int gettimeofday(struct timeval* tv, struct timezone* tz);//原型
#include<sys/time.h>//標頭檔案,如果成功獲取當前時間, 那麼返回0, 否則返回 - 1, 錯誤程式碼存於errno中
//gettimeofday() 會把目前的時間通過tv所指的結構返回, 並將當地時區的資訊放到tz所指的結構中。
struct timeval{
	long tv_sec;//存放的是秒
	long tv_usec;//存放的是微秒
};
struct timezone {
	int tz_minuteswest;//存放的是與格林威治時間的時差
	int tz_destime;//存放的是時間的修正方式
};
*/
/*
#include<stdio.h>
#include<sys/time.h>
int main(int argc, char* argv[])
{
	struct timeval start, end;
	gettimeofday(&start, NULL);
	double timeuse;
	int j;
	for (j = 0; j < 1000000; j++);
	gettimeofday(&end, NULL);
	timeuse + 1000000 * (end.tv_sec - start.tv_sec) + end.tv_usec-start.tv_usec;
	timeuse /= 1000000;
	printf("執行時間爲:%f\n", timeuse);
	return 0;
}
*/
/*
#include<stdio.h> 
void main()
{
	volatile int i = 10;
	int a = i;
	printf("i= %d\n", a);
	__asm {
		mov dword ptr[ebp - 4], 20h
	}
	int b = i;
	printf("i= %d\n", b);
}
*/
/*
volatile int i = 10;
int a = i;
。。。//其他程式碼,並未明確告訴編譯器,對i進行過操作
int b = i;

volatile 指出 i是隨時可能發生變化的,每次使用它的時候必須從i的地址中讀取,因而編譯器生成的

彙編程式碼會重新從i的地址讀取數據放在b中。而優化做法是,由於編譯器發現兩次從i讀數據的程式碼之間

的程式碼沒有對i進行過操作,它會自動把上次讀的數據放在b中。而不是重新從i裏面讀。這樣以來,如果

i是一個暫存器變數或者表示一個埠數據就容易出錯,所以說volatile可以保證對特殊地址的穩定存取。
*/


/*
__asm { mov dword ptr [ebp-4], 50h}
ebp-4是個地址,這個地址指向的記憶體是一個dword型變數,將0x50這個數值放到這個變數中。
*/


/*
關鍵字volatile有什麼含意 ? 並給出三個不同的例子。
一個定義爲volatile的變數是說這變數可能會被意想不到地改變,這樣,編譯器就不會去假設這個變數的值了。精確地說就是,優化器在用到這個變數時必須每次都小心地重新讀取這個變數的值,而不是使用儲存在暫存器裡的備份。下面 下麪是volatile變數的幾個例子:
•; 並行裝置的硬體暫存器(如:狀態暫存器)
•; 一箇中斷服務子程式中會存取到的非自動變數(Non - automatic variables)
•; 多執行緒應用中被幾個任務共用的變數



回答不出這個問題的人是不會被僱傭的。我認爲這是區分C程式設計師和嵌入式系統程式設計師的最基本的問題。搞嵌入式的傢夥們經常同硬體、中斷、RTOS等等打交道,所有這些都要求用到volatile變數。不懂得volatile的內容將會帶來災難。
假設被面試者正確地回答了這是問題(嗯,懷疑是否會是這樣),我將稍微深究一下,看一下這傢夥是不是直正懂得volatile完全的重要性。
•; 一個參數既可以是const還可以是volatile嗎?解釋爲什麼。
•; 一個指針可以是volatile 嗎?解釋爲什麼。
•; 下面 下麪的函數有什麼錯誤:
int square(volatile int *ptr)
{
return *ptr * *ptr;
}

下面 下麪是答案:
•; 是的。一個例子是隻讀的狀態暫存器。它是volatile因爲它可能被意想不到地改變。它是const因爲程式不應該試圖去修改它。
•; 是的。儘管這並不很常見。一個例子是當一箇中服務子程式修該一個指向一個buffer的指針時。
•; 這段程式碼有點變態。這段程式碼的目的是用來返指針*ptr指向值的平方,但是,由於*ptr指向一個volatile型參數,編譯器將產生類似下面 下麪的程式碼:


int square(volatile int *ptr)
{
int a,b;
a = *ptr;
b = *ptr;
return a * b;
}


由於*ptr的值可能被意想不到地該變,因此a和b可能是不同的。結果,這段程式碼可能返不是你所期望的平方值!正確的程式碼如下:

long square(volatile int *ptr)
{
int a;
a = *ptr;
return a * a;
}
*/

12.while(–a)和while(a–)

/*
#include<stdio.h>
int main(void)
{
	int a = 3, sum = 0;
	do
	{
		sum += a * a;

	} while (--a);			//與whil(a--)效果相同
	printf("%d\n", sum);	//sum=14
}
*/

/*
#include<stdio.h>
#include<stdlib.h>
int main()
{
	int a = 3, sum = 0;
	while (a--)				//與while(--a)效果相同
	{
		sum += a * a;

	};
	printf("%d\n", sum);	//sum=5
	exit(0);				//執行exit( ) 函數只能夠返回到main( ) 函數的呼叫處,執行exit() 函數之後將直接結束整個程式的執行
							//當exit() 中的參數爲0時, 表示正常退出,其他返回值表示非正常退出
							//執行exit() 函數意味着進程結束; 而return僅表示呼叫堆疊的返回, 其作用是返回函數值, 並且退出當前執行的函數體, 返回到函數的呼叫處
							//在main() 函數中, return n和exit(n) 是等價的
}
*/

/*
#include <stdio.h>
int main(void)
{
	char ch;
	int i;
	float fl;
	fl = i = ch = 'C'; 
	printf("ch = %c, i = %d, fl = %2.2f\n", ch, i, fl); 
	ch = ch + 1; 
	i = fl + 2 * ch; 
	fl = 2.0 * ch + i; 
	printf("ch = %c, i = %d, fl = %2.2f\n", ch, i, fl); 
	ch = 1107; 
	printf("Now ch = %c\n", ch); 
	ch = 80.89;
	printf("Now ch = %c\n", ch);
	return 0;
}
*/

13.單鏈表


/*
#include<stdio.h>
#include <stdlib.h>
#include <memory.h>

struct list
{
	int data;
	struct list *pNext;
};

struct list * create_list(int);
void new_node(struct list *, struct list *);
void insert_head(struct list *, struct list *);
void traverse_list(struct list *);
void traverse_list2(struct list *);
int delete_node(struct list *, int);
void reverse_linkedlist(struct list *);

int main(void)
{
	struct list *pHead = create_list(0);

	//new_node(pHead, create_list(1));
	//new_node(pHead, create_list(2));
	//new_node(pHead, create_list(3));

	insert_head(pHead, create_list(11));
	insert_head(pHead, create_list(12));
	insert_head(pHead, create_list(13));
	



/
	//建立第一個節點
	struct list *p = (struct list *)malloc(sizeof(struct list));
	if (NULL == p)
	{
		printf("malloc error\n");
		return -1;
	}

	memset(p, 0, sizeof(struct list));
	p->data = 1;
	p->pNext = NULL;

	pHead = p;

	//建立第二個節點
	struct list *p1 = (struct list *)malloc(sizeof(struct list));
	if (NULL == p1)
	{
		printf("malloc error\n");
		return -1;
	}

	memset(p1, 0, sizeof(struct list));
	p1->data = 2;
	p1->pNext = NULL;

	p->pNext = p1;

	//建立第三個節點
	struct list *p2 = (struct list *)malloc(sizeof(struct list));
	if (NULL == p2)
	{
		printf("malloc error\n");
		return -1;
	}

	memset(p2, 0, sizeof(struct list));
	p2->data = 3;
	p2->pNext = NULL;

	p1->pNext = p2;
/

printf("第一個節點的數據:%d\n", pHead->data);
printf("第二個節點的數據:%d\n", pHead->pNext->data);
printf("第三個節點的數據:%d\n", pHead->pNext->pNext->data);
printf("第四個節點的數據:%d\n", pHead->pNext->pNext->pNext->data);

traverse_list2(pHead);
//delete_node(pHead, 12);



reverse_linkedlist(pHead);
traverse_list2(pHead);

return 0;
}

struct list * create_list(int data)
{
	struct list *p = (struct list *)malloc(sizeof(struct list));
	if (NULL == p)
	{
		printf("malloc error.\n");
		return NULL;
	}
	// 清理申請到的堆記憶體
	memset(p, 0, sizeof(struct list));
	// 填充節點
	p->data = data;
	p->pNext = NULL;

	return p;
}

void new_node(struct list *pH, struct list *pnew)
{
	int count = 0;
	//尾部加節點
	struct list *p = pH;
	while (NULL != p->pNext)
	{
		p = p->pNext;
		count++;		
		//第一次pH->pNext=NULL所以第一次不執行while回圈,也就保證了第一個節點的數據初始時爲0,往後再新增節點時纔開始計數
	}
	p->pNext = pnew;
	pH->data = count + 1;
}

void insert_head(struct list *pH, struct list *pnew)
{
	//插入頭節點
	struct list *p = pH;
	pnew->pNext = p->pNext;
	p->pNext = pnew;
	pH->data += 1;
}

void traverse_list(struct list *pH)
{
	//遍歷節點
	int count = 1;
	struct list *p = pH->pNext;
	//printf("shkdjf %d\n", p->data);
	while (NULL != p->pNext)
	{
		//相當於除了頭指針和頭節點之外預設至少有一個有效節點,因爲頭指針爲pH,頭節點爲pH->pNext,第一個有效節點爲pH->pNext->pNext
		//printf("shkdjf %d\n", p->data);
		//第一次判斷pH->pNext->pNext(第三個節點)
		//第二次判斷pH->pNext->pNext->pNext(第四個節點)
		//第三次判斷pH->pNext->pNext->pNext->pNext(爲NULL,不執行回圈)
		count += 1;
		printf("第【%d】個節點數據爲【%d】\n", count, p->data);
		//第一次是pH->pNext->data
		//第二次是pH->pNext->pNext->data
		//之所以沒有列印出最後一個是因爲,下邊的p = p->pNext;才把p指針移動到下一位
		p = p->pNext;
	}
	//printf("第【%d】個節點數據爲【%d】\n", count+1, p->data);
}

void traverse_list2(struct list *pH)
{
	struct list *p = pH;
	while (NULL != p->pNext)
	{
		p = p->pNext;
		printf("節點爲%d\n", p->data);
	}
}

int delete_node(struct list *pH, int data)
{
	//刪除節點
	struct list *p = pH;
	struct list *pPreview = NULL;
	while (NULL != p->pNext)
	{
		pPreview = p;
		p = p->pNext;
		if (p->data == data)
		{
			if (NULL == p->pNext)
			{
				pPreview->pNext = NULL;
			}
			else
			{
				pPreview->pNext = p->pNext;
			}
			return 0;
		}
	}
		printf("not found the node");
		return -1;
}

void reverse_linkedlist(struct list *pH)
{
	//列表逆序
	struct list *p = pH->pNext;
	struct list *pBack;
	if (NULL == p->pNext || NULL == p)
	{
		return ;
	}
	else
	{
		while (NULL != p->pNext)
		{
			pBack = p->pNext;
			if (p == pH->pNext)
			{
				p->pNext = NULL;
			}
			else
			{
				p->pNext = pH->pNext;
			}
			pH->pNext = p;
			p = pBack;
		}
		insert_head(pH, p);
	}
	
}
*/

14.雙鏈表

/*

#include<stdio.h>
#include <stdlib.h>

struct node
{
	int data;
	struct node *pPrev;
	struct node *pNext;
};

struct node *create_list(int);
void insert_tail(struct node *, struct node *);
void insert_head(struct node *, struct node *);
void head_traverse(struct node *);
void tail_traverse(struct node *);
int delete_node(struct node *, int );

int main(void)
{
	struct node *pHeader = create_list(0);

	//insert_tail(pHeader, create_list(1));
	//insert_tail(pHeader, create_list(2));
	//insert_tail(pHeader, create_list(3));

	insert_head(pHeader, create_list(11));
	insert_head(pHeader, create_list(12));
	insert_head(pHeader, create_list(13));

	printf("node is【%d】\n", pHeader->data);
	printf("node 1 is【%d】\n", pHeader->pNext->data);
	printf("node 2 is【%d】\n", pHeader->pNext->pNext->data);
	printf("node 3 is【%d】\n", pHeader->pNext->pNext->pNext->data);
	tail_traverse(pHeader);
	//struct node *p = pHeader->pNext->pNext->pNext;
	//head_traverse(p);
	printf("1*********1\n");
	delete_node(pHeader, 13);
	tail_traverse(pHeader);

	return 0;
}

struct node *create_list(int data)
{
	//建立節點
	struct node *p = (struct node *)malloc(sizeof(struct node));
	if (NULL == p)
	{
		printf("malloc error\n");
		return NULL;
	}
	else
	{
		p->data = data;
		p->pPrev = NULL;
		p->pNext = NULL;
	}
	return p;
}
void insert_tail(struct node *pH, struct node *pNew)
{
	//尾插入節點
	struct node *p = pH;
	while (NULL != p->pNext)
	{
		p = p->pNext;
	}
	p->pNext = pNew;
	pNew->pPrev = p;
}
void insert_head(struct node *pH, struct node *pNew)
{
	//頭節點插入
	int count = 0;
	pNew->pNext = pH->pNext;
	if (NULL != pH->pNext)
	{
		count++;
		pH->pNext->pPrev = pNew;
	}
		pH->pNext = pNew;
		pNew->pPrev = pH;
		pH->data = count + 2;
}
void head_traverse(struct node *pTail)
{
	//頭遍歷節點(前向遍歷)
	struct node *p = pTail;
	while (NULL != p->pPrev)
	{
		printf("node is【%d】\n", p->data);
		p = p->pPrev;

	}

}
void tail_traverse(struct node *pH)
{
	//尾遍歷節點(後向遍歷)
	struct node *p = pH;
	while (NULL != p->pNext)
	{
		p = p->pNext;
		printf("node is【%d】\n", p->data);

	}

}
int delete_node(struct node *pH, int data)
{
	//刪除節點
	struct node *p = pH;

	while (NULL != p->pNext)
	{
		p = p->pNext;
		if (p->data == data)
		{
			if (NULL == p->pNext)
			{
				p->pPrev->pNext = NULL;
			}
			else
			{
				p->pPrev->pNext = p->pNext;
				p->pNext->pPrev = p->pPrev;
			}
			free(p);
			return 0;
		}
	}
	printf("not found node\n");
	return -1;
}
*/

15.猴子吃桃

//將一堆桃子平均分成5份正好多1個,第一次拿掉其中的一份後+1個後,再把剩下的平分5份正好多一個,一直這樣,第五次,正好拿了n個,至少有多少個桃
/*
#include<stdio.h>
int taozi(int);

int main(void)
{
	int n;
	printf("請輸入第5次拿的個數n:");
	scanf_s("%d", &n);
	taozi(n);
}
int taozi(int n)
{
	int sum = 4*n;
	//int sum = n;
		for (int i = 0; i < n; i++)
		{
			//sum = (sum) / (4 / 5) + 1;
			sum = (sum * 5) / 4 + 1;
			//sum = (sum - 1) * 5 + 1;
		}
		printf("桃子的總數爲:%d\n", sum);
		return 0;
}
*/


/*
#include<stdio.h>
int main(void)
{
	int n;
	double sum ;
	printf("請輸入第5次拿的個數n:");	//n=255
	scanf_s("%d", &n);
	sum = 4 * (double)n;
	//printf("桃子的總數爲:%d\n", sum);
	for (int i = 0; i < 5; i++)
	{
		sum = sum*1.25 + 1.0;
	}
	printf("桃子的總數爲:%f\n", sum);
}
*/
/*
#include<stdio.h>
#include<math.h>
int main(void)
{
	int i = 0, n = 1, t = n;//n是總的桃子數
	for (i = 0; i < 5; i++)
	{
		if (n % 5 == 1)
			n = n - (n / 5 + 1);
		else
			i =-1, n = ++t;//執行完這一句之後就要執行for回圈中的i++;爲了保證從i=0開始5次回圈,所以先執行一次i=-1;這樣i++後i=0
		//不管前面執行了幾次回圈,只要有一次不符合if就執行els這樣i之前無論是幾,執行else後就成爲-1,再執行i++,i=0又重新回圈判斷

	}
	printf("桃子的總數爲:%d\n", t);
}
*/

16.階乘

//遞回求階乘
/*
#include<stdio.h>
int jiecheng(int);
int main(void)
{
	int a;
	printf("請輸入一個數:");
	scanf_s("%d", &a);
	printf("%d的階乘爲%d", a, jiecheng(a));

}
int jiecheng(int n)
{
	if (0 == n || 1 == n)
		return 1;
	else
		return n * jiecheng(n - 1);
}
*/

//用for回圈求
/*
#include<stdio.h>
void jiecheng(int);
int main(void)
{
	int a;
	printf("請輸入一個數:");
	scanf_s("%d", &a);
	jiecheng(a);

}
void jiecheng(int n)
{
	int m = n;
	int c=1;
	if (0 == n || 1 == n)
	{
		c = 1;
		printf("%d的階乘爲%d", m, c);
	}
	else
	{
		for (int i = 2; i <= n; i++)
		{
			c = c * i;
		}
		printf("%d的階乘爲%d", m, c);
	}
}

*/

17.矩陣乘法

/*
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char const *argv[])
{
	int** A;
	int** B;
	int r_A;
	int c_A;
	int r_B;
	int c_B;
	printf("The row of A:");
	scanf_s("%d", &r_A);
	printf("The col of A:");
	scanf_s("%d", &c_A);
	printf("The row of B:");
	scanf_s("%d", &r_B);
	printf("The col of B:");
	scanf_s("%d", &c_B);
	if (c_A != r_B) {
		printf("Wrong Matrix!\n");
		return 0;
	}
	A = (int**)malloc(r_A * sizeof(int*));
	for (int i = 0; i < r_A; i++) {
		A[i] = (int*)malloc(c_A * sizeof(int));
	}
	B = (int**)malloc(r_B * sizeof(int*));
	for (int i = 0; i < r_B; i++) {
		B[i] = (int*)malloc(c_B * sizeof(int));
	}
	printf("Now input the A.\n");
	for (int i = 0; i < r_A; i++) {
		for (int j = 0; j < c_A; j++) {
			scanf_s("%d", &(A[i][j]));
		}
	}
	printf("Now input the B.\n");
	for (int i = 0; i < r_B; i++) {
		for (int j = 0; j < c_B; j++) {
			scanf_s("%d", &(B[i][j]));
		}
	}
	int** ret;
	ret = (int**)malloc(r_A * sizeof(int*));
	for (int i = 0; i < r_A; i++) {
		ret[i] = (int*)malloc(c_B * sizeof(int));
	}
	for (int i = 0; i < r_A; i++) {
		for (int j = 0; j < c_B; j++) {
			ret[i][j] = 0;
			for (int k = 0; k < c_A; k++) {
				ret[i][j] += (A[i][k] * B[k][j]);
			}
		}
	}
	for (int i = 0; i < r_A; i++) {
		for (int j = 0; j < c_B; j++) {
			printf("%d\t", ret[i][j]);
		}
		printf("\n");
	}
	for (int i = 0; i < r_A; i++) {
		free(A[i]);
	}
	free(A);
	for (int i = 0; i < r_B; i++) {
		free(B[i]);
	}
	free(B);
	for (int i = 0; i < r_A; i++) {
		free(ret[i]);
	}
	free(ret);

	return 0;
}


*/

/*
#include <stdio.h>
int main()
{
	int a[3][2] = { 2,3,4,5,3,5 };
	int b[2][3] = { 1,2,3,4,5,6 };
	int c[3][3];
	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 3; j++)
		{
			c[i][j] = 0;
			for (int k = 0; k < 2; k++)
				c[i][j] += a[i][k] * b[k][j];
		}
	}
	printf("矩陣1與矩陣2相乘的結果爲:\n");
	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 3; j++)
		{
			printf("%d\t", c[i][j]);
		}
		printf("\n");
	}
	return 0;
}
*/

18.型別轉換



/*
#include<stdio.h>
int main(void)
{
	unsigned short i = 0;
	unsigned int a = 0;
	unsigned int b = 0;
	unsigned short j = 0;
	//unsigned long j = 0;
	while (i <unsigned short(j - 1))
		//j-1會隱式轉換成int型別
	{
		printf("執行while succeed");
		break;
	}
}
*/

19.冒泡選擇排序

/*
#include<stdio.h>

int main(void)
{

}


//氣泡排序
//比較相鄰兩個數
void bubble_sort(int arr[],int n)		
{
	int i, j, flag, temp;
	for (i = 0; i < n - 1; i++)
	{
		flag = 1;					//flag的作用見下面 下麪註釋
		for (j = 0; j < n - i - 1; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				temp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = temp;
				flag = 0;
			}
		}
		if (1 == flag)
			break;
	}
	printf(".......");
}
*/
/*
第三輪結束之後的比較結果就是最終的結果了, 沒有必要再進行接下來的比較操作了, 但是計算機並不知道, 它還會進行接下來的比較操作。 爲了改進冒泡法排序的這一缺點, 防止對已經完成
排序功能的數據進行沒有任何意義的比較操作, 我們定義一個識別符號flag, 在每輪比較操作之前將flag賦值爲1, 如果在該輪比較操作中發現排序沒有完成, 那麼就在該輪結束時將其賦值爲0, 
否則不改變其flag值, 在下一輪開始前對flag進行判斷,如果其值爲1, 表示在上一輪排序中發現已經完成了排序功能, 那麼就結束冒泡法排序的比較操作。
*/

/*
//選擇排序
//每次都比較第一個數和後面的數
void choice_sort(int a[], int n);
{
	int i, j, k, temp;
	for (i = 0; i < n - 1; i++)
	{
		k = 0;
		for (j = i + 1; j < n; j++)
		{
			if (a[j] < a[k])
			{
				k = j;
			}
			if (k != i)
			{
				temp = a[i];
				a[i] = a[k];
				a[k] = temp;
			}
		}
	}
}
*/

20判斷大小端

/*
//第一種方法強制型別轉換
#include<stdio.h>
int main(void)
{
	int a = 0x11223344;
	int* p;
	p = &a;
	if (0x44 == *(char*)p)	//必須將p轉換成char型
	{
		printf("小端模式");
	}
	else
	{
		printf("大端模式");
	}
}
*/

/*
//第二種方法,共用體
	//共用體的特點:
	//1.union中可以定義多個成員,union的大小由最大的成員的大小決定;
	//2.union成員共用同一塊大小的記憶體,一次只能使用其中的一個成員;
	//3.對某一個成員賦值,會覆蓋其他成員的值;
	//4.聯合體union的存放順序是所有成員都從低地址開始存放。
#include <stdio.h>

union bit {		//對齊原則,char與int指向的均是低位地址 
	int a;
	char b;
};

int main() {
	bit test;
	test.a = 0x12345678;
	if (test.b == 0x78)		//本質還是強制型別轉換
		printf("本機爲小端模式\n");
	else
		printf("本機爲大端模式\n");
	return 0;
}
*/

/*
//第三種 
void checkCpuMode(void)
 {
  int a = 0x12345678;
  if((char)a == 0x12)
   printf("big endian\n");
  else
   printf("little endian\n");
 }
*/

21.陣列首地址表示方法

/*
也就是說, &c+1表示了該陣列最後一個元素的下一個地址。,而&c 與 c 或者 &c[0] 一致,都表示該陣列的首地址。
c和&c[0]都表示陣列首元素首地址,c+1和&c[0]+1表示陣列下一個元素的地址
&c表示整個陣列的首地址,&c+1表示整個陣列地址+1;
*/
/*
#include<stdio.h>
int main(void)
{
	int a[5];
	printf("a = %d\n", a);
	printf("&a[0] = %d\n", &a[0]);
	printf("&a = %d\n", &a);
	printf("===========================\n");
	printf("a+1 = %d\n", a+1);
	printf("&a[0]+1 = %d\n", &a[0]+1);
	printf("&a+1 = %d\n", &a+1);
	return 0;
}


輸出結果
a = 16448460
& a[0] = 16448460
& a = 16448460
==========================
a + 1 = 16448464
& a[0] + 1 = 16448464
& a + 1 = 16448480
*/

22.陣列作爲結構體變數

/*
#include<stdio.h>
struct PP { float x, y, z; };
int main() {
	struct PP s[3] = { {1,4,5},{3,2,4},{5,3,7} };//陣列作爲結構體變數時有兩種給陣列賦值得得方式,一種是像這樣定義時整體賦值,
			//第二種是定義完之後再在後面程式中賦值時,只能像第9行那樣對每個元素賦值,而不能再整體賦值
	int i;
	printf("before:\n");
	//s[0].x = 1; s[0].y = 4; s[0].z = 5;
	for (i = 0; i < 3; i++) printf("%g %g %g\n", s[i].x, s[i].y, s[i].z);
	printf("after: \n");
	s[0].z=0; s[1].z=0; s[2].z=0; //變化
	for (i = 0; i < 3; i++) printf("%g %g %g\n", s[i].x, s[i].y, s[i].z);

	return 0;
}
*/

/*
struct rtc_time
{
	unsigned int year;
	unsigned int month;
	unsigned int date;			// 幾號
	unsigned int hour;
	unsigned int minute;
	unsigned int second;
	unsigned int day;			// 星期幾
};

struct rtc_time tWrite =
	{
		.year = 2015,
		.month = 8,
		.date = 9,
		.hour = 18,
		.minute = 11,
		.second = 3,
		.day = 0,
	};
*/

23.陣列作爲結構體成員

/*
#include<stdio.h>
#include<string.h>
int main(void)
{
typedef struct student {
		char name[20];
		char address[20];
		char school[30];
		int age;
		float score;
		char number[50];
	}stu;
stu a;
strcpy_s(a.name, "李明");
strcpy_s(a.address, "河北");
strcpy_s(a.school, "college");
a.age = 20;
a.score = 82.5;
strcpy_s(a.number, "1704102");
printf("name is %s\naddres is %s\nschool is %s\nage is %d\nscore is %f\nnumber is %s\n", a.name, a.address, a.school, a.age, a.score, a.number);
}
*/

/*
#include<stdio.h>
#include<string.h>

void shuzu_jiegouti();

int main(void)
{
	shuzu_jiegouti();
}

void shuzu_jiegouti()
{
	typedef struct student {
		char name[20];
		char address[20];
		char school[30];
		int age;
		float score;
		char number[50];
	}stu;
	stu a;
	strcpy_s(a.name, "李明");
	strcpy_s(a.address, "河北");
	strcpy_s(a.school, "college");
	a.age = 20;
	a.score = 82.5;
	strcpy_s(a.number, "1704102");
	printf("name is %s\naddres is %s\nschool is %s\nage is %d\nscore is %f\nnumber is %s\n", a.name, a.address, a.school, a.age, a.score, a.number);
}
*/
/*
#include<stdio.h>
#include<string.h>

typedef struct student {
	char name[20];
	char address[20];
	char school[30];
	int age;
	float score;
	char number[50];
}stu;

stu a;
void shuzu_jiegouti(struct student);

int main(void)
{
	shuzu_jiegouti(a);
}

void shuzu_jiegouti(stu a)
{
	//stu a;
	strcpy_s(a.name, "李明");
	strcpy_s(a.address, "河北");
	strcpy_s(a.school, "college");
	a.age = 20;
	a.score = 82.5;
	strcpy_s(a.number, "1704102");
	printf("name is %s\naddres is %s\nschool is %s\nage is %d\nscore is %f\nnumber is %s\n", a.name, a.address, a.school, a.age, a.score, a.number);
}
*/

24.陣列作爲形參

/*
#include<stdio.h>
int fun(char* x)
{
	printf("%d\n", x);
	char* y = x;
	while (*y++);		//將指針y移置字串最末端。即y指向'\0'
	printf("%d\n", y);
	return (y - x - 1);	//y - x爲整個字串 + '\0'的長度。y - x - 1即爲字串的長度。等效於strlen()函數。
}
void main()
{
	char a[] = "hello";
	printf("%c\n", a[0]);
	a[0]++;
	printf("%c\n", a[0]++);
	printf("%c\n", a[0]);
	printf("%d", fun(a));
}
*/

25.野指針

/*
#include<stdio.h>
int main(void)
{
	int* const p;//沒有對p初始化,所以p爲野指針
	*p = 4;
}
*/

26.指針存取二維陣列

/*
#include<stdio.h>
int main(void)
{
	int a[3][3];
	int i, j;
	int* pa;
	printf("直接用二維陣列a中的元素\n");       
	for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 3; j++)
		{
			a[i][j] = i * 3 + j + 1;
			printf("a[%d][%d]=%d\t", i, j, a[i][j]);
		}
		printf("\n");
	}
	pa = a[0];
	printf("%d\n", *pa);
	printf("%d\n", pa);
	printf("%d\n", pa+8);
	printf("%d\n", &a[2][2]);
	printf("通過指針參照二維陣列a中的元素\n");
	for (i = 0, j = 0; pa <= &a[2][2]; pa++)
	{
		printf("a[%d][%d]=%d\t", i, j, a[i][j]);
		j++;
		if (3 == j)
		{
			j = 0;
			i++;
			printf("\n");
		}
	}
}
*/
//通過指針陣列來參照陣列時, 只要把陣列中每行起始元素的首地址指給指針陣列中的每個元素, 就可以實現對陣列的參照了。
//在使用陣列指針參照陣列時, 只需要將陣列元素的首地址賦值給定義的陣列指針即可

27.指針分配動態記憶體

/*
#include<stdio.h>
#include<stdlib.h>
void main()
{
	int* pa = (int*)malloc(sizeof(int) * 5);		//int型別佔4個位元組 5*4=20位元組
	int i;
	for (i = 0; i < 5; i++)							//指針型別佔4個位元組 4*5<=20
	{
		*(pa + i) = i + 1;
		printf("*(*pa+%d))=%d\n", i, *(pa + i));
	}
	free(pa);
}
*/
//切記不能對還沒有指向記憶體的指針直接賦值(數值指針)
//編譯器在編譯的過程中會在記憶體中爲字串分配一個記憶體區域, 同時將分配的記憶體區域的首地址傳遞給指針變數, 因此在通過指針定義字串時不必爲其
//分配記憶體空間, 編譯器在編譯的過程中會自動完成對字串記憶體的分配, 這與之前講解的一般數值指針不同。
/*
#include<stdio.h>
int main(void)
{
	int a;
	int* pa = &a;
	*pa = 30;
	printf("*pa=%d\n", *pa);		//*pa所指向的記憶體空間是a所在的記憶體,所以不用釋放記憶體,也不能釋放,否則會報錯
}
*/

28.指針函數與函數指針

//指針函數
/*
#include<stdio.h>
int* p1(int, int);
int main(void)
{
	int* p;
	p = p1(1, 2);
	printf("p1 result is %d\n", *p);
	return 0;
}
int* p1(int a, int b)
{
	int sum = a + b;
	return &sum;
}
*/

/*
//函數指針
#include<stdio.h>
int p1(int, int);
int main(void)
{
	int (*p2)(int a, int b);
	p2 = p1;		//也可以爲p2=&p1;
	//p1是函數名,即是函數的首地址,所以可以p2=p1;
	//p2爲int (*)(int a, int b)型別,p1爲int (int a, int b)型別,所以可以p2=&p1;
	printf("add result is %d\n", p2(200, 300));
	return 0;
}
int p1(int a, int b)
{
	int sum = a + b;
	return sum;
}
*/

29.指針陣列

/*
#include<stdio.h>
int main(void)
{
	char a[5] ;//等效於char* (&a) [5];
	char (*pa)[5],*pb;
	pa = &a;	//&a並不是一個char型指針,而是一個char*[5]型別的指針,所以定義*pa時不能定義成char *pa;,而應該定義成第6行那樣
	pb = &a[0]; //&a[0]是一個char型的指針,就相當於char變數
	printf("char型陣列指針pa做佔用的記憶體大小爲:% d\n",sizeof( * pa) );		//sizeof(*一維陣列指針)返回的是陣列單個元素對應型別的大小1*5=5
	printf("char型陣列指針pb做佔用的記憶體大小爲:% d\n", sizeof(*pb));
	printf("字元陣列指針 佔%d個位元組\n", sizeof(pa));					   //sizeof(一維陣列指針)返回的是陣列單個元素指針的大小,即系統指針的長度,32位元系統爲4,64位元系統位8
	printf("字元陣列指針 佔%d個位元組\n", sizeof(pb));
	printf("pa=%d\tpa+1=%d\n", pa, pa + 1);
	printf("pb=%d\tpb+1=%d\n", pb, pb + 1);;
	return 0;
}
*/

30.質數、最小公倍數、最大公約數、閏年

/*
判斷x是否爲質數
從2一直到x-1
x如果有質因數,肯定會小於x/2,所以從2到x/2
除了2以外的質因數都是奇數,所以三從3開始一直到x/2的所有奇數
其實只要從2一直嘗試到根號x就可以了。因爲x只要有因數必定有一個因數小於等於根號x
當x是質數的時候,x的所有的倍數必然是合數,如果x已經判斷不是質數了,那麼x的倍數也不是質數
*/

/*************************第一種*****************************/

/*
#include<stdio.h>
int main(void)
{
int i,j;
for(i=2;i<100;i++)
{
for(j=2;j<=(i/j);j++)   //其實只要從2一直嘗試到根號x就可以了。因爲x只要有因數必定有一個因數小於等於根號x
{
	if(!(i%j))          //i整除j,說明i不是質數,break
break;
}

if(j>(i/j))             //比如i=5時執行for(j=2;j<=(i/j);j++),第一次j=2,滿足條件,不滿足if,不break,j++,此時j=3,所以j>(i/j)即3>(5/3),與之前for回圈中2<=(5/2)相對應
printf("%d",i);         //類似下面 下麪用k是否爲0來判斷
}
return 0;
}
*/

/**************************第二種****************************/

/*
#include<stdio.h>
int main(void)
{
int i,j,k=0;
for(i=2;i<100;i++)
{
	 k=0;
for(j=2;j<=(i/j);j++)   //其實只要從2一直嘗試到根號x就可以了。因爲x只要有因數必定有一個因數小於等於根號x
{
	if(i%j==0)          //i整除j,說明i不是質數,break
		{k=1;
	break;}
}
//if(j>(i/j))
if(k==0)
printf("%d\n",i);
}
return 0;
*/


/*
閏年
整百年時須是400的倍數,不是整百年時,須是4的倍數,閏年時2月份爲29天,比平年多一天
思路;先判斷是否爲4的倍數,再判斷是否爲100的倍數,不是100的倍數則爲閏年
若爲100的倍數,须再次判斷是否爲400的倍數,是400的倍數則爲閏年
*/
/*
#include<stdio.h>
int run_year(int);
int main(void)
{
	int n;
	printf("請輸入年份:");
	scanf_s("%d", &n);
	run_year(n);
}
int run_year(int a)
{
	if ((0 == a % 100 && 0 == a % 400) || (0 != a % 100 && 0 == a % 4))
		printf("%d年是閏年", a);
	else
		printf("%d年不是閏年", a);
	return 0;
}
*/



/*
最小公倍數
除0以外,可以被這兩個數同時整除的最小的數,如2和4的最小公倍數爲4,6和9的最小公倍數爲27
最小公倍數=兩整數的乘積/兩整數的最大公約數
思路:先求最大公約數,再求最小公倍數
1、a%b餘數爲c
2、c=0;b即爲最大公約數,a即爲最小公倍數
3、c!=0;a=b;b=c;重複回到步驟1
*/
/*
#include<stdio.h>
int max_gongbei(int,int);
int main(void)
{
	int a, b;
	printf("請輸入兩個數:");
	scanf_s("%d%d", &a, &b);
	max_gongbei(a, b);
}
int max_gongbei(int a, int b)
{
	int c = a % b,m,n;
	m = a;
	n = b;
	while (0 != c)
	{
		a = b;
		b = c;
		c = a % b;
	}
	printf("%d和%d的最大公約數爲%d\n", m, n, b);
	printf("%d和%d的最小公倍數爲%d\n",m,n,((m*n)/b));
	return 0;
}
*/
/*
最大公約數的第二種方法,但推薦上面用的那種方法
*/
/*
#include<stdio.h>
int main()
{
	int m, n, temp, i;
	printf("Input m & n:");
	scanf_s("%d%d", &m, &n);
	if (m < n)  //比較大小,使得m中儲存大數,n中儲存小數
	{ //交換m和n的值
		temp = m;
		m = n;
		n = temp;
	}
	for (i = n; i > 0; i--)  //按照從大到小的順序尋找滿足條件的自然數
		if (m % i == 0 && n % i == 0)
		{//輸出滿足條件的自然數並結束回圈
			printf("The GCD of %d and %d is: %d\n", m, n, i);
			break;
		}

	return 0;
}
*/

31.字串逆序輸出

/*
#include<stdio.h>
#include<string.h>

void Reverse(char *);
void Reverse1(char *, char *);
void Reverse2(char *);

int main(void)
{
	char str[100];
	char ptr[100];
	printf("Input a string:");
	gets_s(str);
	//Reverse(str);
	//Reverse1(str,ptr);
	Reverse2(str);
	//printf("Inversed results:%s\n", str);
	//printf("Inversed results:%s\n", ptr);
	printf("Inversed results:%s\n", str);

	return 0;
}


//第一種
void Reverse(char *str)
{
	char *s1, *s2,temp;
	int n = strlen(str);
	s1 = str;
	s2 = s1 + n - 1;
	while (s1<s2)
	{
		temp = *s1;
		*s1 = *s2;
		*s2 = temp;
		s1++;
		s2--;
	}
}


//第二種(逆序遍歷陣列)
void Reverse1(char *str,char *ptr)
{
	int n = strlen(str);
	printf("字串長度爲%d\n", n);
	int i = 0;
	for (i = 0; i <= n-1; i++)
	{
		ptr[i] = str[n - i - 1];
	}
	ptr[i] = '\0';
}

//第三種(與第一種方法本質相同,都是第i個與第n-i-1個數交換)
void Reverse2(char *str)
{
	int n = strlen(str);
	int i;
	char temp;
	for (i = 0; i < n / 2; i++)
	{
		temp = str[i];
		str[i] = str[n - i - 1];
		str[n - i - 1] = temp;
	}
}
*/

32.字串常數與字元陣列

/*
#include <stdio.h>
#include <string.h>
int main() {
    char str[] = "http://c.biancheng.net";
    int len = strlen(str), i;
    //直接輸出字串
    printf("%s\n", str);
    //每次輸出一個字元
    for (i = 0; i < len; i++) {
        printf("%c", str[i]);
    }
    printf("\n");
    return 0;
}


執行結果:
http ://c.biancheng.net
http://c.biancheng.net

字元陣列歸根結底還是一個數組,上節講到的關於指針和陣列的規則同樣也適用於字元陣列。更改上面的程式碼,使用指針的方式來輸出字串:
*/



/*
#include <stdio.h>
#include <string.h>
int main() {
    char str[] = "http://c.biancheng.net";
    char* pstr = str;
    int len = strlen(str), i;
    //使用*(pstr+i)
    for (i = 0; i < len; i++) {
        printf("%c", *(pstr + i));
    }
    printf("\n");
    //使用pstr[i]
    for (i = 0; i < len; i++) {
        printf("%c", pstr[i]);
    }
    printf("\n");
    //使用*(str+i)
    for (i = 0; i < len; i++) {
        printf("%c", *(str + i));
    }
    printf("\n");
    return 0;
}


執行結果:
http ://c.biancheng.net
http://c.biancheng.net
http://c.biancheng.net


*/



/*
除了字元陣列,C語言還支援另外一種表示字串的方法,就是直接使用一個指針指向字串,例如:

char* str = "http://c.biancheng.net";

或者:

char* str;
str = "http://c.biancheng.net";



字串中的所有字元在記憶體中是連續排列的,str 指向的是字串的第 0 個字元的地址;我們通常將第 0  個字元的地址稱爲字串的首地址。字串中每個字元的型別都是char,所以 str 的型別也必須是char * 。

下面 下麪的例子演示瞭如何輸出這種字串:


#include <stdio.h>
#include <string.h>
int main() {
    char* str = "http://c.biancheng.net";
    int len = strlen(str), i;

    //直接輸出字串
    printf("%s\n", str);
    //使用*(str+i)
    for (i = 0; i < len; i++) {
        printf("%c", *(str + i));
    }
    printf("\n");
    //使用str[i]
    for (i = 0; i < len; i++) {
        printf("%c", str[i]);
    }
    printf("\n");
    return 0;
}



執行結果:
http ://c.biancheng.net
http://c.biancheng.net
http://c.biancheng.net

*/


/*

這一切看起來和字元陣列是多麼地相似,它們都可以使用% s輸出整個字串,都可以使用* 或[]獲取單個字元,這兩種表示字串的方式是不是就沒有區別了呢?

有!它們最根本的區別是在記憶體中的儲存區域不一樣,字元陣列儲存在全域性數據區或棧區,第二種形式的字串儲存在常數區。全域性數據區和棧區的字串(也包括其他數據)有讀取和寫入的許可權,而常數區的字串(也包括其他數據)只有讀取許可權,沒有寫入許可權。

記憶體許可權的不同導致的一個明顯結果就是,字元陣列在定義後可以讀取和修改每個字元,而對於第二種形式的字串,一旦被定義後就只能讀取不能修改,任何對它的賦值都是錯誤的。

我們將第二種形式的字串稱爲字串常數,意思很明顯,常數只能讀取不能寫入。請看下面 下麪的演示:


#include <stdio.h>
int main() {
    char* str = "Hello World!";
    str = "I love C!";  //正確
    str[3] = 'P';  //錯誤
    return 0;
}



這段程式碼能夠正常編譯和鏈接,但在執行時會出現段錯誤(Segment Fault)或者寫入位置錯誤。

第4行程式碼是正確的,可以更改指針變數本身的指向;第5行程式碼是錯誤的,不能修改字串中的字元。

*/



/*
到底使用字元陣列還是字串常數

在程式設計過程中如果只涉及到對字串的讀取,那麼字元陣列和字串常數都能夠滿足要求;如果有寫入(修改)操作,那麼只能使用字元陣列,不能使用字串常數。

獲取使用者輸入的字串就是一個典型的寫入操作,只能使用字元陣列,不能使用字串常數,請看下面 下麪的程式碼:


#include <stdio.h>
int main() {
    char str[30];
    gets_s(str);
    printf("%s\n", str);
    return 0;
}



執行結果:
C C++ Java Python JavaScript
C C++ Java Python JavaScript
*/