- typedef的用法
typedef的作用是真正意義上地定義一種別名,而不是一種簡單的宏替換。
typedef 與 #define的區別:
案例一:
通常講,typedef要比#define要好,特別是在有指標的場合。請看例子:
typedef char *pStr1;
#define pStr2 char *;
pStr1 s1, s2;
pStr2 s3, s4;
在上述的變數定義中,s1、s2、s3都被定義為char *,而s4則定義成了char,不是我們所預期的指標變數,根本原因就在於#define只是簡單的字串替換而typedef則是為一
個類型起新名字。
注意語句char* pa,pb; 其中pa定義成了一個字元指標,而pb則是定義成了一個字元變數。這個問題我一直沒有注意過,對於int來說,是一樣的,比如int *a,b實際定義了一個指標和一個整型數。所以好的編程習慣應該是將*與變數名串連在一起,而不是緊跟在類型後。比如:char *pa, pb;這個就比較一目瞭然了。
再考慮這個問題:
下面的代碼中編譯器會報一個錯誤,你知道是哪個語句錯了嗎?
typedef char * pStr;
char string[4] = "abc";
const char *p1 = string;
const pStr p2 = string;
p1++;
p2++;
答案是:p2++會報錯。
const char *p1 = string 意思是說一個指向const char類型的指標。。
p1指標是一個變數。他可以指向任意的char對象。就算不是const定義的char也可以,p1可以指向不同的char對象,因為他是一個變數。但他無論是指向const char對象還是char對象。都不能改變其指向對象的值。就是說上例子p1不能對string做修改。只能讀取string的值。
而
typedef char *pStr
const pStr p2 = string 這裡是typedef和指標共用時的坑爹指出。
const pStr p2 = string
pStr const p2 = string
char* const p2 = string 上面3個語句是一個意思。
就是說p2是一個指向非const、char類型的對象的const指標。p2初始化之後就不能夠指向第二個對象。但p2可以對string的值進行修改。
對於用typedef定義函數指標別名的格式,以前自己沒有見過,有必要看看。
typedef void(*SIG_TYPE)(int); 定義了一個函數指標SIG_TYPE,函數的傳回型別是void,參數為int。
2. 關於指標。 轉載一篇別人的文章。
指標是C語言的精華,它是一柄“雙刃劍”,用的好與壞就看使用者的功力了。下面就一道面試題,看一下指標與數組的區別。
char *p1, *p2;
char ch[12];
char **pp;
p1 = ch;
pp = &ch;
p2 = *pp;
問p1和p2是否相同
題目如上,找出其中的不妥之處。
首先,數組ch是沒有初始化的。其次,一個比較隱含的地方是,數組名可以代表數組第一個元素的首地址,這個沒有問題,但是,數組名並非一個變數,數組分配 完成後,數組名就是固定的,地址也是固定的。這樣導致的結果就是絕對不能把數組名當作變數來進行處理。上述題目中,pp=&ch,顯然是把數組名 當作指標變數來使用了,這樣肯定出問題。
這個題目存在的兩個問題,第一個問題比較簡單,可以認為是粗心大意。但是第二個問題就是相當複雜了,擴充開來,那就是C語言中的精華中的指標和數組的聯絡 與區別問題了。
下面分為兩步,首先看一下指標和數組的區別方法,然後提出對上述程式的修改方案。
1 指標和數組的區別
(1)指標和數組的分配
數組是開闢一塊連續的記憶體空間,數組本身的標識符(也就是通常所說的數組名)代表整個數組,可以使用sizeof來獲得數組所佔據記憶體空間的大小(注意, 不是數組元素的個數,而是數組佔據記憶體空間的大小,這是以位元組為單位的)。舉例如下:
#include <stdio.h>
int main(void)
{
char a[] = "hello";
int b[] = {1, 2, 3, 4, 5};
printf("a: %d\n", sizeof(a));
printf("b memory size: %d bytes\n", sizeof(b));
printf("b elements: %d\n", sizeof(b)/sizeof(int));
return 0;
}
數組a為字元型,後面的字串實際上佔據6個位元組空間(注意最後有一個\0標識字串的結束)。從後面sizeof(b)就可以看出如何獲得數組佔據的內 存空間,如何獲得數組的元素數目。至於int資料類型分配記憶體空間的多少,則是編譯器相關的。gcc預設為int類型分配4個位元組的記憶體空間。
(2)空間的分配
這裡又分為兩種情況。
第一,如果是全域的和靜態
char *p = “hello”;
這是定義了一個指標,指向rodata section裡面的“hello”,可以被編譯器放到字串池。在彙編裡面的關鍵字為.ltorg。意思就是在字串池裡的字串是可以共用的,這也是 編譯器最佳化的一個措施。
char a[] = “hello”;
這是定義了一個數組,分配在可寫資料區塊,不會被放到字串池。
第二,如果是局部的
char *p = “hello”;
這是定義了一個指標,指向rodata section裡面的“hello”,可以被編譯器放到字串池。在彙編裡面的關鍵字為.ltorg。意思就是在字串池裡的字串是可以共用的,這也是 編譯器最佳化的一個措施。另外,在函數中可以返回它的地址,也就是說,指標是局部變數,但是它指向的內容是全域的。
char a[] = “hello”;
這是定義了一個數組,分配在堆棧上,初始化由編譯器進行。(短的時候直接用指令填充,長的時候就從全域字串表拷貝),不會被放到字串池(同樣如前,可 能會從字串池中拷貝過來)。注意不應該返回它的地址。
(3)使用方法
如果是全域指標,用於不需要修改內容,但是可能會修改指標的情況。
如果是全域數組,用於不需要修改地址,但是卻需要修改內容的情況。
如果既需要修改指標,又需要修改內容,那麼就定義一個數組,再定義一個指標指向它就可以了。
2 我編寫的修改方案
[armlinux@lqm pointer]$ cat pointer.c
/*
* Copyright 2007 (c), Shandong University
* All rights reserved.
*
* Filename : test.c
* Description: about pointer
* Author : Liu Qingmin
* Version : 1.0
* Date : 2007-08-27
*/
#include <stdio.h>
/*
* define a macro which is used to debug array mode and pointer mode.
* if 1, debug array mode; else debug pointer mode.
* You can change it according to your decision.
*/
#define ARRAY_OR_POINTER 0
int main(void)
{
char *p1;
char *p2;
char **pp;
//test1
#if ARRAY_OR_POINTER
char ch[] = "hello, world!\n";
printf("%d, %d, %d, %d\n", sizeof(p1), sizeof(p2),
sizeof(pp), sizeof(ch));
#else
char *ch = "hello, world!\n";
printf("%d, %d, %d, %d\n", sizeof(p1), sizeof(p2),
sizeof(pp), sizeof(ch));
#endif
//test2
p1 = ch;
#if ARRAY_OR_POINTER
pp = &p1;
#else
pp = &ch;
#endif
p2 = *pp;
if (p1 == p2) {
printf("p1 equals to p2\n");
} else {
printf("p1 doesn't equal to p2\n");
}
return 0;
}
執行結果如下:
// ARRAY_OR_POINTER為0時
[armlinux@lqm pointer]$ ./test
4, 4, 4, 4
p1 equals to p2
// ARRAY_OR_POINTER為1時
[armlinux@lqm pointer]$ ./test
4, 4, 4, 15
p1 equals to p2
如果使用了數組定義方式,而又使用pp=&ch,那麼就會出現類似下面的錯誤:
[armlinux@lqm pointer]$ make
gcc -Wall -g -O2 -c -o pointer.o pointer.c
pointer.c: In function `main':
pointer.c:44: warning: assignment from incompatible pointer type
gcc -Wall -g -O2 pointer.o -o test
[armlinux@lqm pointer]$ ./test
4, 4, 4, 15
p1 doesn't equal to p2
主要就是,數組定義時,數組名的類型應該是char (*)[12],這個與char**是有區別的。所以如果需要對數組名進行其他動作的話,最好先用一個指標承接。
3. struct的儲存空間大小
一般按照四位對齊,不及四位的話會補齊。
4. 函數指標
下面程式中關於函數指標的使用都是合法的。
#include <iostream>
using namespace std;
float f(const float&);
int main()
{
typedef float (*PFUN)(const float &);
PFUN p1 = f;
PFUN p2 = &f;
cout<<p1(1)<<endl;
cout<<(*p1)(1)<<endl;
cout<<p2(1)<<endl;
cout<<(*p2)(1)<<endl;
}
float f(const float& a)
{
return a+1;
}
程式的輸出為:
2
2
2
2
如何返回指向函數的指標呢?比如: int (*ff(int)) (int *,int );在這個,ff是一個函數,它有一個int類型的形參,返回一個指向函數的指標。指標指向的函數傳回值為int類型,接受一個int*型和一個int型的形參。這個表達看上去比較複雜,可以使用typedef簡化:typedef int (*PF)(int*,int);PF ff(int);