C語言實例

  • 編譯環境:Visual Studio 2012
  • 編程語言:C

1、memcpy與’/0’

int main(void) {
  char* p1 = "abc";
  char* p2 = (char*)malloc(sizeof(char) * 3);
  char* p3 = (char*)memcpy(p2, p1, 3);
  printf("p2 = %s\np3 = %s\n", p2, p3);
  free(p2);
  p2 = NULL;
  p3 = NULL;
  system("pause");
  return 1;
}

打印結果:亂碼

p2 = abc?
p3 = abc?
請按任意鍵繼續. . .


int main(void) {
  char* p1 = "abc";
  char* p2 = (char*)malloc(sizeof(char) * 4);
  char* p3 = (char*)memcpy(p2, p1, 4);
  printf("p2 = %s\np3 = %s\n", p2, p3);
  free(p2);
  p2 = NULL;
  p3 = NULL;
  system("pause");
  return 1;
}

打印結果:正常

p2 = abc
p3 = abc
請按任意鍵繼續. . .

2、volatile的使用

  1. 並行設備的硬件寄存器(如:狀態寄存器)

  2. 一箇中斷服務子程序中會訪問到的非自動變量(如下例子)

  3. 多線程應用中被幾個任務共享的變量

int time = 0;
int main()
{
	if(time == 10)
	{
		dothing();//不會正確執行
	}
}
void isr_tick()
{
	time = 10;
}

例子中dothing()可能永遠不會執行,一直使用的是“tme = 0”的副本i,不是讀取i原始地址的值;time在中斷中改變後main函數不會讀取到。

有位博主說:volatile應該解釋爲“直接從原始內存地址讀值”比較合適,“易變的”這種解釋有點誤導人。這個理解不錯。

3、數字轉字符

void itoa ( int n,char s[])
{
	int i,j,sign;//數字太大要使用unsigned long
	char tmp;
	
	if((sign=n)<0)//負數時使用
	{
		n=-n;
	}

	i=0;
	do
	{
		s[i++]=n%10+'0';//從個位開始 ,循環取個位 十位 百位...放進s[0] s[1] s[2]
	}
	while ((n/=10)>0);

	if(sign<0)  //負數時使用
	{
		s[i++]='-';
	}
	s[i]='\0';  //字符串要加終止符

	if(i <= 0) return;
	for(j=0;j<=(i/2);j++)//FROM 0~(i/2),之前是倒序,這裏再逆序排一遍
	{
		tmp = s[j];
		s[j] = s[i-j-1];
		s[i-j-1] = tmp; 
	}
}

4、memcpy len 與指針加減 len 的區別

定義int a2[ ],則a2[ ]中的每個元素佔一個int,一般機器上也就是4個字節。
而memcpy第三個參數爲字節數,所以:
memcpy(a1,a2, sizeof(int) * 10 ); //複製了40個字節,賦值正確
memcpy(a1,a2,10); //只複製了10個字節,賦值異常

void main(void) {
	int i = 0;
	int a2[] = {1,2,3,4,5,6,7,8,9,10};
	int a1[10];
	memcpy(a1,a2, sizeof(int)*10);
	while(i<10)
	{
		printf("%d\n",a1[i]);
		i++;
	}
}

自定義的格式也要用sizeof():

	typedef unsigned short uint16;
	
	uint16 a2[] = {1,2,3,4,5,6,7,8,9,10};
	uint16 a1[10];
	memcpy(a1,a2, sizeof(uint16 )*10);

這裏再提一下指針的加減,[a2 + 2]不是代表a2後移多少個字節,而是代表指針後移兩個元素,即*a1 = a2[2] = 3。

	uint16 a2[] = {1,2,3,4,5,6,7,8,9,10};
	uint16 *a1;
	a1 = a2 + 2; 
	printf("%d\n",*a1);

下面示例可以幫助理解以上內容:
eg:把二維數組第0行100個元素拷貝給a0,第1行100個元素拷貝給a1:

	uint16 a[2][100] =;
	uint16 a0[100];
	uint16 a1[100];
	//把二維數組第0行100個元素拷貝給a0:
	memcpy(a0,a, sizeof(uint16 )*100);//此時指針a指向a[0][0]元素--//sizeof(uint16 )*100代表字節數
	//把二維數組第1行100個元素拷貝給a1:
	a = a + 100;//此時指針a指向a[1][0]元素--//100代表元素數
	memcpy(a1,a, sizeof(uint16 )*100);

5、sizeof(結構體) 的計算 (結構體對齊)

編譯器在編譯程序時會遵循以下原則:
(1)結構體變量中成員的偏移量必須是成員大小的整數倍(0被認爲是任何數的整數倍)
(2)結構體大小必須是所有成員大小的整數倍,也即所有成員大小的公倍數。
(3)結構體大小等於最後一個成員的偏移量加上其字節大小。

如下: 表示結構體元素的實際偏移量, 之前的數表示填充, 之後的數表示元素佔用:

typedef struct _hello
{
	unsigned short b;//(2)  offset:0√ 1
	unsigned int c;//(4) offset:2 3 4√ 5 6 7
	char a;//(1) offset:8√
	//結構體大小=8+1=9,不符合:10 11 12√
}hello;

typedef struct _hello1
{
	unsigned int c;//(4) offset:0√ 1 2 3
	unsigned short b;//(2)  offset:4√ 5
	char a;//(1) offset:6√
	//結構體大小=6+1=7,不符合:8√
}hello1;

typedef struct _hello2
{
	char a;//(1) offset:0√
	unsigned short b;//(2)  offset:1 2√ 3
	unsigned int c;//(4) offset:4√ 5 6 7
	//結構體大小=4+4=8√
}hello2;

typedef struct _hello3
{
	char a;//(1) offset:0√
	unsigned short b;//(2)  offset:1 2√ 3
	unsigned int c;//(4) offset:4√ 5 6 7
	unsigned int d;//(4) offset:8√ 9 10 11
	unsigned short e;//(2)  offset:12√ 13
	char f;//(1) offset:14√ 
	char g;//(1) offset:15√
	unsigned short h;//(2)  offset:16√ 17
	unsigned int i;//(4) offset:18 19 20√ 21 22 23
	//結構體大小=20+4=24√
}hello3;

void main(void) {
	printf("hello = %d\n",sizeof(hello));
	printf("hello1 = %d\n",sizeof(hello1));
	printf("hello2 = %d\n",sizeof(hello2));
	printf("hello3 = %d\n",sizeof(hello3));
}

輸出結果如下:

hello = 12
hello1 = 8
hello2 = 8
hello3 = 24

6、回調函數

typedef  void (*funcpointer)(char*) ;  //funcpointer就是我們自定義的【函數指針】類型

void callbackfunc(char* ch) //回調函數
{
   static int usedtimes = 0;
   usedtimes ++;
   printf("%s\n",ch);
}
 
void directfunc(char *s,funcpointer a) //直接調用的函數
{
	a(s);
	printf("直接使用directfunc,directfunc通過函數指針回調callbackfunc\n");
}

void directfunc2(funcpointer a) //直接調用的函數
{
	char *data = "new directfunc running";
	a(data);
	printf("可以用不同的函數 調用 同一個回調函數\n");
}

int main(void) {
	char *s1 ="hello world";
	char *s2 ="p hello world";
	directfunc(s1,callbackfunc);
	directfunc(s2,&callbackfunc);//這裏也證明【函數名】也是【指針】
	directfunc2(callbackfunc);
	directfunc2(&callbackfunc);
}

運行結果:

hello world
直接使用directfunc,directfunc通過函數指針回調callbackfunc
p hello world
直接使用directfunc,directfunc通過函數指針回調callbackfunc
new directfunc running
可以用不同的函數 調用 同一個回調函數
new directfunc running
可以用不同的函數 調用 同一個回調函數
請按任意鍵繼續. . .

第一個函數callbackfunc:回調函數,我們寫好這個函數後不會直接去調用它,只會使用它的函數指針(callbackfunc或者&callbackfunc)。
第二個函數directfunc:直接使用的函數,我們把回調函數的指針傳給它,它自己去調用回調函數。
第三個函數directfunc2:直接使用的函數,我們把回調函數的指針傳給它,它自己去調用回調函數。

當然,所有的函數具體要執行什麼功能都是程序員自己寫的。回調的好處是這兩個函數可以由不同的程序員在不同的時空去寫,寫好了只需告知回調函數的接口就行。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章