- 編譯環境: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的使用
-
並行設備的硬件寄存器(如:狀態寄存器)
-
一箇中斷服務子程序中會訪問到的非自動變量(如下例子)
-
多線程應用中被幾個任務共享的變量
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:直接使用的函數,我們把回調函數的指針傳給它,它自己去調用回調函數。
當然,所有的函數具體要執行什麼功能都是程序員自己寫的。回調的好處是這兩個函數可以由不同的程序員在不同的時空去寫,寫好了只需告知回調函數的接口就行。