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