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指向静态区中的值,没有赋值到函数栈中, 因此对指针的内容进行修改会产生错误。

这个问题详细解释: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;
}


转载于:https://www.cnblogs.com/huruzun/p/5423881.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值