C语言重要知识点回顾

来源:互联网 发布:淘宝店可以卖吗 编辑:程序博客网 时间:2024/04/28 22:23

太久没有看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;}


1 0
原创粉丝点击