C語言重要知識點回顧
太久沒有看C語言相關知識了,開始要整理回顧一些重點知識點啦,因為各大公司筆試還是有許多C語言相關的題,做個複習。
const 關鍵字與指標修飾使用
普通指標使用:
//普通指標使用,我們通過 i 或者 p 指標都能改變變數值
void test1()
{
int i = 1;
int * p = &i;
printf("p=%d\n",*p);
i = 2;
printf("p=%d\n",*p);
(*p)++;
printf("p=%d\n",*p);
printf("i=%d\n",i);
}
輸出結果:
這個結果是我們好理解的。
接著 const int *p 問題
// const int *p 表示p 所指的物件是隻讀不可以改變的,但p 指標可以指向其他地址
void test2()
{
int i = 1;
int j = 100;
const int * p = &i;
printf("p=%d\n",*p);
i = 2;
printf("p=%d\n",*p);
p = &j;
printf("p=%d\n",*p);
/*
(*p)++;// 出錯 error:increment of read-only lacation '*p'
printf("p=%d\n",*p);
*/
}
輸出結果:
這裡我們發現指標p 我們可以隨便調整指向哪塊已知的記憶體空間,但是不能通過 給*p 複製來改變指標所指的物件。
int const *p 和上面const int *p 效果一樣這裡就不多說啦。
接下來說 int * const p 形式,如下測試程式碼:
// int * const p 表示指標p 不可修改,但是指標p 所指向的內容可以修改 void test3() { int i = 1; int j = 100; int * const p = &i; printf("p=%d\n",*p); i = 2; printf("p=%d\n",*p); /* p = &j;// error:assignment of read-only variable 'p' printf("p=%d\n",*p); */ (*p)++; printf("p=%d\n",*p); printf("i=%d\n",i); }
輸出結果:
最後一種情況就是上面情況結合在一起const int * const p 這樣就是p 指標無法修改,p 指標所指的內容也無法修改。
C與指標第六章習題
1.
char * find_char(char const * source , char const *chars)
{
if(source==NULL || chars==NULL)
return NULL;
char const * cp;
for(;*source!='\0';source++)
{
// 這裡每次遍歷chars 中內容
for(cp=chars;*cp!='\0';cp++)
{
if(*source == *cp)
return (char *)source;
}
}
return NULL;
}
實現中發現一個問題:char a[] 與 char *a 的區別
char a[]在執行時賦值,值會從靜態區賦值到函式的棧中,對它進行修改不會產生任何問題。char *a在編譯時就確定了,a指向靜態區中的值,沒有賦值到函式棧中, 因此對指標的內容進行修改會產生錯誤。
char * match(char * str,char const *substr)
{
while(*substr != '\0')
{
if(*str++ != *substr++)
return NULL;
}
return str;
}
int Del_substr(char *str,char const *substr)
{
char * next;
char * orig = str;
while(*str != '\0'){
next = match(str,substr);
if(next != NULL)
break;
str++;
}
if(*str == NULL)
return 0;
printf("outside\n");
while((*str) != '\0')
{
*str = *next;
str++;
next++;
}
printf("%s\n",orig);
return 1;
}
3.
void reverse_string(char *str)
{
if(str == NULL)
return;
char *p = str;
int count = 0;
char ch;
for(;*p!='\0';p++)
{
count++;
}
p = str;
char * end = p + count -1;
while(p < end)
{
ch = *p;
*p = *end;
*end = ch;
p++;
end--;
}
*(str+count) = '\0';
printf("%s\n",str);
}
指向陣列的指標VS指標陣列
指向陣列的指標:
int vector[10], *vp = vector; 這個宣告是合法的,它為整型陣列分配記憶體,並把vp 宣告為指向整型的的指標。
int matrix[2][3] matrix 並不是指向整型的的指標,而是一個指向整型陣列的指標,我們應該如何宣告指向陣列的指標?
int (*mp)[3]這裡要帶上第二維的資料控制,不是mp指標自增操作不確定能跳過多少長度。
int matrix[2][3] = {{1,2,3},{4,5,6}};
int *p = &matrix[0][0];
printf("%d\n",*p);
printf("%d\n",*++p);
printf("%d\n",*++p);
如上程式碼,指標p 指向陣列中第一個元素,然後指標自增1 ,指向了第二個數字,所以上面輸出就是:1,2,3 ,我們一直要確定好一件事情就是指標型別,因為型別決定了指標自增1是能跳動多大的距離。
int matrix[2][3] = {{1,2,3},{4,5,6}};
int (*mp)[3];
mp = matrix;
printf("%d\n",(*mp)[0]);
printf("%d\n",(*mp)[1]);
printf("%d\n",(*++mp)[0]);
如上程式碼:定義mp 為指向擁有3個整型元素的陣列的指標,當對mp 與整數相加時,該整數值根據3這個長度調整,所以mp++ 導致指標mp 指向陣列下一行陣列元素。
所以上述程式碼輸出:1,2,4 這裡就可以告訴我們如何去對二維陣列元素通過指標進行操作。
指標陣列:
正如你可以建立整型陣列一樣,你也可以宣告指標陣列,如下面:
int *api[10] ,api 有十個元素,每個元素是指向int 型的指標。
再看個複雜點結構:
char const *keyword[] = {
"do",
"for",
"if",
"register",
"return",
"switch",
"while"
};
keyword 是一個指標陣列,陣列中每個元素都指向一個char型陣列。當我們需要查詢某個關鍵字時可以遍歷該指標陣列,如下:
int lookup_keyword(char const * const desired_word,char const *keyword_table[],int const size)
{
char const **kwp;
/*
char (*p)[10];// 這裡搞清楚型別啊,keyword_table 是char **
*/
// 查詢kewword_table中每個單詞
for(kwp = keyword_table;kwp < keyword_table + size;kwp++){
printf("%s\n",*kwp);
*kwp = "hello";//陣列中內容可以改變,所以*kwp 可以指向別的內容
if(strcmp(desired_word,*kwp) == 0){
return kwp - keyword_table;
}
}
return -1;
}
這裡需要注意為什麼kwp 定義為指標的指標? 分析一下,keyword_table 是陣列起始位置是指標,而陣列中元素也是指標,所以當要引用陣列中元素時必須定義為指標的指標來遍歷該陣列。
如果上述結構定義為二維陣列這樣:
char const keywordMatrix[][9]={
"do",
"for",
"if",
"register",
"return",
"switch",
"while"
};
實現上述查詢相同功能則需要進行改動:
int lookup_keywordMatrix(char const * const desired_word,char const (*keyword_table)[9],int const size)
{
char const (*kwp)[9];
for(kwp=keyword_table;kwp<keyword_table+size;kwp++)
{
if(strcmp((char *)kwp,desired_word) == 0)
{
return kwp - keyword_table;
}
}
return -1;
}
首先傳參就需要改變,這裡定義的 char const(*keyword_table)[9] 是指向char型陣列的指標,定義kwp同樣需要這樣為:char const (*kwp)[9] ,
所以在使用strcmp 函式時需要型別強制轉換。
小結:
陣列名是指向陣列第一個元素的指標。這裡有兩個例外,sizeof返回整個陣列佔用的位元組而不是一個指標所佔用的位元組。
int a[] 對 &a 操作返回是指向整個陣列的指標。
指標和陣列不相等。當我們宣告一個數組時,同時就分配了記憶體空間,但是宣告一個指標時,只是分配了容納指標本身空間。
當陣列名作為函式引數傳遞的,實際傳遞給函式是指向陣列第一個元素的指標。 函式所接收的引數實際為原引數的拷貝,所以函式可以對其進行操縱不影響實際引數,但是執行期間修改陣列元素會影響原先陣列元素。
結構體與記憶體分配:
結構體最基本的兩種訪問方式,關於記憶體分配,C語言中使用是malloc 和 free 。
malloc函式從記憶體池中提取一塊合適的記憶體,並向呼叫程式返回一個指向這塊記憶體的指標。你需要自己手動對這塊記憶體進行初始化,malloc函式分配是一塊連續的記憶體,
使用malloc函式時一定要注意malloc分配記憶體空間是否成功,如果不成功malloc函式會返回NULL,所以好的程式設計習慣一定是檢查分配記憶體空間。
此外malloc函式返回是void * 指標,因為這個返回型別問題,我們使用malloc經常會需要強制型別轉換。
動態記憶體常見錯誤:
NULL指標解引用操作、分配記憶體操作越界、釋放並非動態分配記憶體、釋放一塊動態分配記憶體的一部分、動態記憶體釋放後繼續使用等。
通過實際對單向連結串列操作來熟悉結構體和記憶體分配。
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
typedef struct LinkList{
int value;
LinkList * next;
}*ListPoint;
void insert_Node(LinkList **head,int value)
{
ListPoint pre,current;
pre = NULL;
current = *head;
while(current && current->value < value)
{
pre = current;
current = current->next;
}
LinkList * new_node = (LinkList *)malloc(sizeof(LinkList));
//好的程式設計習慣需要每次分配記憶體檢查
if(new_node == NULL)
{
printf("malloc memory error !!!");
return;
}
new_node->value = value;
new_node->next = current;
// 意味著插入連結串列起始位置
if(pre == NULL)
{
printf("test here\n");
*head = new_node;
}
else{
pre->next = new_node;
}
}
void Print_LinkList(LinkList *head)
{
if(head == NULL)
{
printf("empty LinkList\n");
return;
}
while(head !=NULL)
{
printf("%d",head->value);
head = head->next;
}
}
int main()
{
int arr[6] = {3,2,1,6,4,5};
ListPoint head = NULL;
for(int i=0;i<6;i++)
{
insert_Node(&head,arr[i]);
}
Print_LinkList(head);
for(int i=0;i<6;i++)
{
printf("haha\n");
}
return 0;
}