C语言中的数组、字符串、函数、结构体

 指针简介

什么是指针?

从根本上看指针是一个值为内存地址的变量(或数据对象),指针变量的值是地址。

与指针相关的运算符

  • & 后接一个变量名时,&给出该变量的地址
  • * 后跟一个指针名或者地址时,*给出存储在指针指向地址上的值

声明指针

声明指针变量时必须指定指针所指向变量的类型,因为不同的变量类型占用不同的存储空间,一些指针的操作要求知道操作对象的大小(例如指针的加法、减法等都是以指向对象为准的)。另外,程序需要知道存储在指定地址上的数据类型。

指针的初始化:

//在指针定义处对指针进行初始化操作
#include<stdio.h>
int main(void)
{
    int a = 10;
    //此处的*p和定义后的*p代表的意义不同
    int *p = &a;
}
//先定义指针后初始化
#include<stdio.h>
int main(void)
{
    int a = 10;
    int *p;
    p=&a;
}

指针的基本操作

  • 赋值操作:可以将地址赋给指针。例如,用数组名、带地址运算符(&)的变量名、另一个指针进行赋值。注意:地址应该和指针类型兼容。也就是说,不能把double类型的地址赋给指向int的指针。
double i=10;
int *p;
p=&i //避免这样做
  • 解引用:*运算符给出指针指向地址上存储的值。注意:不能解引用一个未初始化的指针。创建一个指针,系统只分配了存储指针本身的内存,并未分配存储数据的内存。因此,在使用指针之前,必须先用已分配的地址初始化它。
int i=3,*p;
*p=i;//不要这样做,因为指针p没有进行初始化
  • 取址:和所有变量一样,指针变量也有自己的地址和值。对于指针而言,&运算符给出指针本身的地址。
  • 指针和整数相加:可以使用+运算符把指针与整数相加,或整数与指针相加。无论哪种情况,整数都会和指针所指向类型的大小(以字节为单位)相乘,然后把结果与初始地址相加。
  • 递增指针:递增指向数组元素的指针可以让指针移动至数组的下一个元素
  • 指针减去一个整数:可以使用-运算符从一个指针中减去一个整数,指针必须是第一个运算对象,整数是第二个运算对象。如果相减的结果超过了初始指针所指向数组的范围,计算结果则是未定义的。除非正好超过数组末尾第一个位置,C保证该指针有效
  • 递减指针
  • 指针求差:通常,求差的两个指针分别指向同一个数组的不同元素,通过计算求出两元素之间的距离。差值的单位与数组类型的单位相同。如果指向两个不同数组的指针进行求差运算可能会得到一个值,或者导致运行时错误。
  • 比较:使用关系运算符可以比较两个指针的值,前提是两个指针指向相同的数据类型的对象。

空指针NULL

在C语言中,如果一个指针不指向任何数据,我们称之为空指针,使用NULL表示。因为空指针没有指向任何数据,所以地址就是随机值。一些函数对空指针是很敏感的(如结构体中的strcpy_s()函数),在使用前判断一下是否为空。

指针和数组

为什么要将指针和数据结合使用?

指针能够有效的处理数组,数组表示法其实是在变相的使用指针。

一维数组和指针

数组名是数组首元素的地址,也就是说如果flizny是一个数组,那么下列的语句成立:

flizny == &flizny[0]

指针表示法和数组表示法 

在C语言中,a[i]和*(a+i)这两个表达式都是等价的。无论a是数组名还是指针变量,这两个表达式都没有问题。但是,只有当a是指针变量时,才能够使用a++这样的表达式。

保护数组中的数据

防止在使用指针的过程中对数组中的元素进行了更改我们需要使用const来保护数组中的数据不被更改。主要有三种方式来保证相关的数据不被更改。

  • const int *p 不改变地址上的值
  • int * const p 不改变指针p
  • const int * const p 不改变地址上的值,也不改变指针p 

二维数组和指针

创建一个二维的数组:

int zippo[4][2]//四行两列的数组

zippo的首元素是一个内含两个int值的数组,数组名zippo是该数组首元素的地址。

#include <stdio.h>

int sum_demo1(int* a, int n);
int sum_demo2(int* start, int* end);

void sum_rows(int ar[][2], int rows);//二维数组行和函数
void sum_cols(int(*p)[4], int rows);//二维数组列和函数
void sum_total(int p[][2], int rows);//二维数组总和函数

int main(void)
{
	//创建一维数组
	int b[5] = { 1,2,3,4,5 };
	int a[] = { 1,2,3,4 }; //省略方框中的数字,让编辑器自动匹配
	int sum_b = 0;
	int sum_a = 0;
	//数组的引用 数组名[下标]、*(b+1)与数组名表达形式相同
	for (int i = 0; i < 5; i++)
	{
		printf("b[%d]=%d\n", i, b[i]);
	}
	for (int i = 0; i < 5; i++)
	{
		printf("b=[%d]=%d\n", i, *(b + i));
	}

	//数组名
	printf("数组名表示首字母地址,a=%p\n", a);
	printf("数组名的地址一样是首字母地址,&a=%p\n", &a);

	//在函数中调用数组
	//因为作为实际参数的数组名是一个指针,所以要求形参是一个与之匹配的形参
	sum_b = sum_demo1(b, 5);
	printf("sum_b=%d\n", sum_b);

	sum_a = sum_demo2(a, (a + 4));
	printf("sum_a=%d\n", sum_a);


	//创建二维数组
	int c[3][2] = { {1,2},{3,4},{5,6} };
	//数组名
	printf("c表示{1,2}地址,c[0]表示{1,2}中1的地址");
	printf("c的地址,&c=%p\n", &c);
	printf("数组名表示首元素地址即{1,2}地址,c=%p\n", c);
	printf("c[0]=%p\n", c[0]);
	printf("数组名表示首个整数地址即{1,2}中{1}的地址,c[0][0]=%p\n", &c[0][0]);
	//数组的引用
	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 2; j++)
		{
			printf("c[%d][%d]=%d\n", i, j, c[i][j]);
			printf("c[%d][%d]=%d\n", i, j, *(c[i] + j));
			printf("c[%d][%d]=%d\n", i, j, *((*(c + i)) + j));
		}
	}
	sum_rows(c, 3);
	sum_cols(c, 3);
	sum_total(c, 3);
}

//一维数组相加函数
int sum_demo1(int* a, int n)
{
	int i;
	int total = 0;
	for (i = 0; i < n; i++)
	{
		//在函数中使用数组表示法,使得意图更加明显
		total += a[i];
	}
	return total;
}
//使用指针算法
int sum_demo2(int* start, int* end)
{
	int total = 0;
	while (start < end)
	{
		total += *start;
		start++;
	}
	return total;
}
//二维数组行和函数
//第一个括号是空的,空的括号表示ar是一个指针
void sum_rows(int ar[][2], int rows)
{
	int rows_total = 0;
	for (int i = 0; i < rows; i++)
	{
		for (int j = 0; j < 2; j++)
		{
			rows_total += ar[i][j];
		}
		printf("rows_total=%d\n", rows_total);
		rows_total = 0;
	}
}
//列和函数
//int(*p)[4]定义一个p指针,指向一个含有四个int类型值的一维数组
void sum_cols(int(*p)[2], int rows)
{
	int cols_total = 0;
	for (int i = 0; i < 2; i++)
	{
		for (int j = 0; j < rows; j++)
		{
			cols_total += p[j][i];
		}
		printf("cols_total=%d\n", cols_total);
		cols_total = 0;
	}
}
//二维数组总和函数
void sum_total(int p[][2], int rows)
{
	int total = 0;
	for (int i = 0; i < rows; i++)
	{
		for (int j = 0; j < 2; j++)
		{
			total += *(*(p + i) + j);
		}
	}
	printf("total=%d\n", total);
}

字符串

函数名作用缺点
printf()将字符串的地址作为参数,依次打印直到结束。
puts()显示字符串,并在末尾添加换行符。
gets()读取整行,直至遇到换行符,然后丢弃换行符,存储其他字符,并在这些字符的末尾添加一个空字符。gets函数输入参数只有words不能检测数组是否能装下输入行。若字符过长将导致缓冲区溢出。
fgets(words,stlen,stdin)

大多用于处理文件输入,第二个参数用于指定输入字符的最大数量。如果该参数的值是n那么读入n-1个字符,或者读到遇到的第一个换行符为止。当输入行不溢出是,fgets函数将换行符放在最后。

第三个参数用于说明读入的文件,如果读入从键盘输入的数据,则以stdin作为参数

fputs(words,stdout)第二个参数指定它要写入的文件。
scanf()以一个空白字符(空行、空格、制表符或换行符)作为字符串的结尾不安全可能会产生溢出的情况。

scanf_s("%s",a,30)

scanf_s(%d,&a)

scanf()函数的改进,给定需要传入的长度,防止溢出,在传入字符时体现出其安全性。
string.h系列函数
strlen()统计字符串的长度
strcat(str1,str2)拼接字符串,传入两个字符串,将str2副本加到str1后面,并返回str1.str1被改变,str2不变str1的大小可能不足以容纳str1和str2产生溢出
strncat(str1,str2,max)上述函数的改进,第三个参数规定str2最多多少个字符可以加到str1
strcmp()检测字符串是否相同,如果两个字符串参数相同,该函数返回0.
strncmp(str1,str2,num)第三个参数限定函数只查找几个字符。
strcpy(copy,orig)拷贝整个字符串
strncpy(copy,orig,num)指定最大拷贝字符数

函数

#include<stdio.h>
int max(int, int);
int min(int, int);
int add(int, int);

int main(void)
{
	int (*p)();//指向函数的指针
	int a, b, c;
	p = max;
	scanf_s("%d %d", &a, &b);
	c = (*p)(a, b);
	printf("a=%d,b=%d,max=%d\n", a, b, c);

	//用指向函数的指针作为函数参数
	//这样在函数中就可以调用其他函数了
	printf("max=");
	progress(a, b, max);
	printf("min=");
	progress(a, b, min);
	printf("sum=");
	progress(a, b, add);
}

int progress(int x, int y, int(*p)())
{
	int i = 0;
	if (p == max)
	{
		i = (*p)(x, y);
		printf("%d\n",i);
	}
	else if (p == min)
	{
		i = (*p)(x, y);
		printf("%d\n", i);
	}
	else if (p = add)
	{
		i = (*p)(x, y);
		printf("%d\n", i);
	}
}
int max(int x, int y)
{
	int i = 0;
	if (x > y)
	{
		i = x;
	}
	else
	{
		i = y;
	}
	return i;
}
int min(int x, int y)
{
	int i = 0;
	if (x > y)
	{
		i = y;
	}
	else
	{
		i = x;
	}
	return i;
}
int add(int x, int y)
{
	int i = 0;
	i = x + y;
	return i;
}

结构体

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define MAXTITLE  41
#define MAXAUTHOR 31
#define SLEN 81

//声明结构体
struct book
{
	char title[MAXTITLE];
	char author[MAXAUTHOR];
	float value;
};

struct off
{
	float first_off;
	float seconf_off;
};

struct buy
{
	struct off buyit;
	char name[30];
	float value;
};

struct namect
{
	char* fname;
	char* lname;
	int letters;
};

char* s_gets(char* st, int n);
void getinfo(struct namect* pst);
void makeinfo(struct namect* pst);
void showinfo(struct namect* pst);
void cleanup(struct namect* pst);

int main(void)
{
	//定义结构变量、初始化结构
	struct book book1 = { "The C PRIMER","Cking",21.1 };
	//访问结构成员
	printf("book1.title=%s\n", book1.title);
	printf("book1.author=%s\n", book1.author);
	printf("book1.value=%f\n", book1.value);

	//结构数组
	struct book library[2] = { {"The C PRIMER","Cking",21.1},{"The Lunix","C_king",21.1} };
	printf("library[0].title=%s\n", library[0].title);
	printf("library[1].title=%s\n", library[1].title);

	//嵌套结构的使用
	struct buy nike_buy = { {0.8,0.7},"nike",500 };
	printf("初始价格为:%f\n", nike_buy.value);
	printf("折上折后的价格为:%lf", nike_buy.buyit.first_off *nike_buy.buyit.seconf_off*nike_buy.value);

	//指向结构的指针
	struct book* p;
	struct book* q;
	p = &book1;
	printf("book1.title=%s\n", (*p).title);
	printf("book1.author=%s\n", (*p).author);
	printf("book1.value=%f\n", (*p).value);

	q = library;
	printf("library[0] address=%p,library[2] address=%p\n", &library[0], &library[1]);
	printf("q:%p,q+1:%p\n", q, q + 1);
	printf("library[0].title=%s\n",(*q).title );
	printf("library[1].title=%s\n",(*(q+1)).title );

	struct namect person;
	getinfo(&person);
	makeinfo(&person);
	showinfo(&person);
	cleanup(&person);
	
	return 0;
}

void getinfo(struct namect* pst)
{
	char temp[SLEN];
	printf("Please enter your first name.\n");
	s_gets(temp, SLEN);
	// 分配内存
	(*pst).fname = (char*)malloc(strlen(temp)*sizeof(char)+1);
	//pst->fname= (char*)malloc(strlen(temp) + 1);
	//将数据写入分配好的内存中
	if ((*pst).fname == NULL)
		return EOF;
	else 
		strcpy_s((*pst).fname, strlen(temp)+1, temp);

	printf("please enter your last name.\n");
	s_gets(temp, SLEN);
	(*pst).lname = (char*)malloc(strlen(temp)*sizeof(char) + 1);
	//pst->fname= (char*)malloc(strlen(temp) + 1);
	//将数据写入分配好的内存中
	strcpy_s((*pst).lname, strlen(temp)+1, temp);
}

void makeinfo(struct namect* pst)
{
	(*pst).letters = strlen((*pst).fname) + strlen((*pst).lname);
}

void showinfo(struct namect* pst)
{
	printf("%s %s,your name contins %d letters.\n", (*pst).fname, (*pst).lname, (*pst).letters);
}

void cleanup(struct namect* pst)
{
	free((*pst).fname);
	free((*pst).lname);
}

char* s_gets(char* st, int n)
{
	char* ret_val;
	char* find;
	ret_val = fgets(st, n, stdin);
	if (ret_val)
	{
		find = strchr(st, '\n');
		if (find)
		{
			*find = '\0';
		}
		else
		{
			while (getchar() != '\n')
				continue;
		}
	}
	return ret_val;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值