太久没有看C语言相关知识了,开始要整理回顾一些重点知识点啦,因为各大公司笔试还是有许多C语言相关的题,做个复习。
普通指针使用:
//普通指针使用,我们通过 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 指针所指的内容也无法修改。
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; }
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); }
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);
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]);
所以上述代码输出:1,2,4 这里就可以告诉我们如何去对二维数组元素通过指针进行操作。
正如你可以创建整型数组一样,你也可以声明指针数组,如下面:
int *api[10] ,api 有十个元素,每个元素是指向int 型的指针。
再看个复杂点结构:
char const *keyword[] = { "do", "for", "if", "register", "return", "switch", "while" };
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; }
如果上述结构定义为二维数组这样:
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; }
所以在使用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; }