1. 程式人生 > >學習C語言的一些需要注意的點

學習C語言的一些需要注意的點

C語言學習

1.指標陣列與二維陣列指標

int *p[3];//指標陣列。
int (*p)[3];//定義了一個指標,指向int[3]這種資料型別,指向二維陣列的指標。

int buf[3][5];      二維陣列名稱,buf代表陣列的首地址
int (*a)[5];        定義一個指向int[5]型別的指標變數a;
a[0],*(a+0),*a;     00列元素的地址;
a+1;                第1行的首地址;
a[1],*(a+1);        第1行,0列元素地址;
a[1]+2,*(a+1)+2,&a[1][2];       第1行,2列元素的地址;
*(a[1
]+2),*(*(a+1)+2),a[1][2]; 第1行,2列元素的值;

2.函式指標

int add(int a, int b)
{
    return a+b;
}
int main()
{
    ......
    int (*p)(int, int);//定義了一個指向函式的指標,可以指向兩個形參都是int,返回值也是int的函式;
    p = add;//直接寫函式的名字,代表函式的地址,將add這個函式的地址賦值給指標變數p;
    int i = 0;
    i = p(5,7);//通過指標變數間接地呼叫指標指向的函式

//==========================================================================
void *p(int ,char*); //函式的宣告,函式名:p,返回值:void* ,引數為:int,char* void (*p)(int ,char*);//定義了一個函式指標p,此函式返回值:void,引數為:int,char*

3.memset,memcpy,memmove
這三個函式分別實現記憶體設定,記憶體拷貝和記憶體移動。
使用memcpy的時候,一定要確保記憶體沒有重疊的區域。
包含

int buf[10] = { 0 };//只能用於定義陣列的時候同時初始化內容。
......
buf[10] = { 0 };//錯誤的語法

memset(buf,0,sizeof
(buf));//第一個引數是要設定的記憶體地址,第二個引數是要設定的值,第三個引數是記憶體大小(單位:位元組)。 //將一塊記憶體初始化為0,最常用的方法。 memcpy(buf2,buf1,sizeof(buf1));//將buf1的內容拷貝到buf2。

3.main函式的引數

int main(int argc,char *argv[])
{
    printf("%d\n", argc);
    //argc代表程式執行的時候有幾個引數,程式名稱本身就是一個引數,所以argc最小值為1。
    //第二個引數是一個指標陣列,其中每個成員的型別是char*
    //argvs是一個指標陣列,argc這個引數就是告訴main函式argv有多少個成員
    return 0;
}

4.register變數、靜態變數

register int i = 0;//建議,如果暫存器有空閒,這個變數就放在暫存器裡面使用
int *p = &i;//錯誤,對於一個register變數,是不能取地址操作的。
......
static int a = 0;//靜態變數,只初始化一次,而且程式執行期間,靜態變數一直存在。

//===========================================================

main.c
static int a = 0;//一旦全域性變數定義static,意思是這個變數只在定義這個變數的檔案內部有效。
//在其他檔案不能用extern關鍵字呼叫。

5.記憶體四區

void test (int a, int b)//形參,棧區
{
    printf("%d,%d\n",&a,&b);
}
int a = 0;//全域性變數,靜態區
int main()//函式,程式碼區
{
    int a = 0;//區域性變數,棧區
    static int i = 0;//靜態變數,靜態區
    return 0;
}
......
char array[1024 * 1024 * 10];//定義一個超大的陣列一定會棧溢位
//=====================================================
//堆區
//堆和棧一樣,也是在程式執行的過程中可以隨時修改的記憶體區域,但沒有棧那樣先進先出的順序。
//堆是一個大容器,它的容量要遠遠大於棧,但是在c語言中,堆記憶體空間的釋放需要手動通過程式碼來完成。

//堆的分配和釋放
int main()
{
    //1.棧陣列
    int array[10] = { 0 };
    //2.堆陣列
    int *p = (int*)malloc(sizeof(int) * 10);//在堆中申請大小為10個int的記憶體空間
    //char *p1 =(char*)malloc(sizeof(char) * 10);//在堆中申請大小為10個char的記憶體空間
    //free(p);//釋放通過malloc分配的記憶體
    //free(p1);//釋放通過malloc分配的記憶體
    //malloc和free成對使用
    memset(p, 0, sizeof(int) * 10);
    int i;
    for (i = 0; i < 10; i++)
    {
        p[i] = i;
    }
    return 0;
}

6.關於堆疊與函式的返回值、以及誤區

int *get_a()//錯誤,不能將一個棧變數的地址通過函式的返回值返回
{
    int a = 0;
    return &a;
}

int *get_a1()//可以通過函式的返回值返回一個堆地址,但是記得一定要free
{
    int *p = (int *)malloc(sizeof(int));//申請了一個堆空間
    return p;
}


//===================================================================

//誤區
void getheap(int *p)
{
    p = (int *)malloc(sizeof(int) * 10);
}//getheap執行完以後,p就消失了,導致他指向的具體的堆空間的地址編號也隨之消失了

int main()
{
    int *p = NULL;
    getheap(p);//實參沒有任何改變,以下的操作都是非法的
    //p[0] = 1;
    //p[1] = 2;
}

//正確的寫法
void getheap(int **p)
{
    *p = (int *)malloc(sizeof(int) * 10);
}//getheap執行完以後,p就消失了,導致他指向的具體的堆空間的地址編號也隨之消失了

int main()
{
    int *p = NULL;
    getheap(&p);//實參沒有任何改變,以下的操作都是非法的
    //p[0] = 1;
    //p[1] = 2;
}

//==================================================================
const char *getString()//正確的模型
{
    return "hello";//常量位於靜態記憶體中
}
int main()
{
    char *p = getString();
    return 0;
}

7.聯合體
如果聯合體中有指標成員,那麼一定要使用完這個指標,並且free指標之後才能使用其他成員。

8.讀寫檔案

fopen("檔案路徑","%模式%");
//"r"  以只讀的方式開啟檔案,該檔案必須存在
//"r+" 以可讀寫的方式開啟檔案,該檔案必須存在
//"rb+" 讀寫開啟一個二進位制檔案,允許讀寫資料,檔案必須存在
//"rw+" 讀寫開啟一個文字檔案,允許讀和寫
//"w" 開啟只寫檔案,若檔案存在則檔案的長度清0,即該檔案的內容會消失,若檔案不存在則建立該檔案
//"w+" 開啟可讀寫檔案,若檔案存在則檔案的長度清0,即該檔案的內容會消失,若檔案不存在則建立該檔案
//"a" 以附加的方式開啟只寫檔案。若檔案不存在,則會建立該檔案,如果檔案存在,寫入的資料會被追加到檔案尾,即檔案原先的內容會被保留。(EOF符保留)
//"a+" 以附加的方式開啟可讀寫檔案。若檔案不存在,則會建立該檔案,如果檔案存在,寫入的資料會被追加到檔案尾,即檔案原先的內容會被保留。(原來的EOF符不保留)

9 . 需要注意的一些函式

//========================================
fopen
fclose
//文字檔案操作函式
    //按照字元讀寫檔案
int getc ( FILE * stream );
int putc ( int character, FILE * stream );

int feof ( FILE * stream );
int fprintf ( FILE * stream, const char * format, ... );
int fscanf ( FILE * stream, const char * format, ... );
    //按照行讀寫檔案
char * fgets ( char * str, int num, FILE * stream );
int fputs ( const char * str, FILE * stream );
//========================================
//二進位制檔案讀寫函式
//fread返回值代表讀取了多少記錄數
    //按照塊讀寫檔案
size_t fread(void*  _Buffer,size_t _ElementSize,size_t _ElementCount,FILE* _Stream);
size_t fwrite(void const* _Buffer,size_t _ElementSize,size_t _ElementCount,FILE* _Stream);

static int stat(char const* const _FileName, struct stat* const _Stat);
//fseek的後返回值0代表成功,-1代表失敗,但如果往後移動檔案指標,超過檔案大小,fseek的返回值還是0,但往前移動超過檔案開始位置返回-1.
int fseek(FILE* _Stream,long  _Offset,int _Origin);
long ftell(FILE* _Stream);
int fflush(FILE* _Stream);
int remove(char const* _FileName);
int rename(char const* _OldFileName,char const* _NewFileName);

//注意一點下程式碼的區別
while (!feof)//此函式會多讀一行
{
    fread(&buf, 1, sizeof(buf), p);
}
while (fread(&buf, 1, sizeof(buf), p))

C語言提高

這幾天看了王保明的C語言提高發現有點亂,整理一下。便於以後複習。深入淺出。

1.第一天 (主要講了C語言一些基本的規律)
1)資料型別的分析

int b[40];
//b+1 &b+1結果不一樣 //b &b所代表的資料型別不一樣
//b代表陣列首元素的地址
//&b代表整個陣列的地址

2.第二天(一級指標記憶體模型及專案開發的重點)

//1>經典字串拷貝函式
void copy_str(char *from, char *to)
{
    //while((*to = *from)!='\0')
    //{
    //  from++;
    //  to++;
    //}

    //while((*to++ = *from++)!='\0')
    //{
    //  ;
    //}

    while(*to++ = *from++);
}

//2>專案開發中的字串模型1:strstr-whiledowhile模型(做字串查詢)

#include<stdio.h>


int getCount_dowhile(char *mystr/*in*/,char *sub/*in*/,int *ncount)
{
    int ret = 0;
    char *p = mystr;//不要輕易改變形參的值
    int tmpCount;

    if (mystr == NULL || sub == NULL || ncount == NULL)
    {
        ret = -1;
        printf("func getCount() err:%d.(mystr == NULL || sub == NULL || ncount == NULL) \n", ret);
        return ret;
    }

    do
    {
        p = strstr(p, sub);
        if (p != NULL)
        {
            tmpCount++;
            p = p + strlen(sub);
        }
        else
        {
            break;
        }
    } while (*p != '\0');

    *ncount = tmpCount;//間接賦值是指標存在的最大意義
    return 0;
}


int getCount_while(char *mystr/*in*/, char *sub/**/, int *ncount)
{
    int ret = 0;
    char *p = mystr;//不要輕易改變形參的值
    int tmpCount;
    if (mystr == NULL || sub == NULL || ncount == NULL)
    {
        ret = -1;
        printf("func getCount() err:%d.(mystr == NULL || sub == NULL || ncount == NULL) \n", ret);
        return ret;
    }

    while (p = strstr(p, sub))
    {
        ncount++;
        p = p + strlen(sub);
        if (*p = '\0')
        {
            break;
        }
    }

    *ncount = tmpCount;//間接賦值是指標存在的最大意義
    return 0;
}

int main()
{
    int ret = 0;
    char *p = "abcd11111abcd2222abcdqqqq";
    char sub[] = "abcd";
    int count = 0;
    ret = getCount(p, sub, &count);
    if (ret != 0)
    {
        printf("func getCount() err:%d\n", ret);
    }
    return 0;
}


//專案開發中的字串模型2:兩頭堵模型.

int trimSpaceStr2( char *p, char *buf2)
{
    int ret = 0;

    int ncount = 0;
    int i, j;
    i = 0;
    j = strlen(p) -1;

    while (isspace(p[i]) && p[i] != '\0')
    {
        i++;
    }

    while (isspace(p[j]) && j>0 )
    {
        j--;
    }

    ncount = j - i + 1;
    //
    strncpy(buf2, p+i, ncount);
    buf2[ncount] = '\0';
    return ret;
}

3.第三天(二級指標記憶體模型及專案開發的重點)

//專案開發中的字串模型3:字串反轉1.
void inverse(char *str)
{
    int length;
    int *p1, *p2;
    if (str == NULL)
    {
        return -1;
    }

    length = strlen(str);
    p1 = str;
    p2 = str + length - 1;

    while (p1 < p2)
    {
        char c = *p1;
        *p1 = *p2;
        *p2 = c;
        p1++;
        p2--;
    }
}

//專案開發中的字串模型3:字串反轉2.

void inverse02(char *p,char *bufresult)
{
    if (p == NULL || bufresult == NULL)
    {
        return;
    }

    if (*p == '\0')
    {
        return;
    }

    inverse02(p + 1, bufresult);
    strncat(bufresult,p,1);
}
陣列大小要注意的地方
    char a[10] = { 0 };
    printf("sizeof(a):%d \t a:%p \nsizeof(&a):%d \t &a:%p \n", sizeof(a), a, sizeof(&a), &a);
    printf("a+1:%p \n&a+1:%p \n", a + 1, &a + 1);

    char* b[10] = { 0 };
    printf("sizeof(b):%d \t b:%p \nsizeof(&b):%d \t &b:%p \n", sizeof(b), b, sizeof(&b), &b);
    printf("b+1:%p \n&b+1:%p \n", b + 1, &b + 1);
//運算結果:
sizeof(a):10    sizeof(&a):4     
a:0116FE34  a+1:0116FE35    
&a:0116FE34 &a+1:0116FE3E

sizeof(b):40    sizeof(&b):4 
b:0116FE04  b+1:0116FE08
&b:0116FE04 &b+1:0116FE2C

總的來說,sizeof(陣列名)可以直接求出陣列的大小,但陣列名指標所代表的步長和首元素大小相等;但對陣列名取地址後的二級指標的步長是整個陣列的大小,sizeof的結果是4