太久沒有看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指向靜態區中的值,沒有賦值到函數棧中, 因此對指針的內容進行修改會產生錯誤。
這個問題詳細解釋:http://blog.chinaunix.net/uid-20583479-id-1920067.html
2.
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;
}