一、C语言笔记归纳总结1基础篇(数组与字符串)

一句话区分p, *p, &p

&p表示编译器为变量p本身分配的内存地址,而p是一个指针变量,它指向的内存地址就用p表示,p指向的地址中的内容就用*p表示。

注: p是一个指针变量的名字,表示此指针变量指向的内存地址,如果使用%p来输出的话,它将是一个16进制数。
*p 表示此指针指向的内存地址中存放的内容,一般是一个和指针类型一致的变量或者常量。
& 是取地址运算符,&p就是取指针p的地址。

1.函数只用一个返回值(单一出口原则),结构清晰,便于修改

2.一个变量既是循环遍历的变量又是条件判断的变量(一专多能是不好的代码)

3.线性搜索没有效率,二分法效率极高,次数为log2 n

4.数组的大小

sizeof给出整个数组所占据的内容的大小,单位是字节

sizeof(a[0]给出数组中单个元素的大小,于是相除就得到了数组的单元个数
//这样的代码,一旦修改数组中初始的数据,不需要修改遍历的代码(安全)

int a[]={0,1,2,3,5};
int length = 0;
length = sizeof(a) / sizeof(a[0]);
 printf("main_数组的长度为: %d\n",length);
// 所有数据的字节数除以一个数据的字节数即为数据的个数

数组作为函数参数时,往往需要传入另一个参数来传入数组的大小
此时不能再用sizeof来计算数组的元素个数,另外在参数数组中
如search(int key,int a[ ])里的[ ]中给出数组大小无效

注:传入函数参数表的数组其实是一个指针
(所以应该直接传值,如:sum(a,length);)
//不能在sum()函数里使用,
其中传递的数组参数和常量指针一样,如:
sizeof(a)=4字节//实际上是在32位编译环境的int型一个指针大小
(32位操作系统,一个字节刚好是8位,因此是4个字节)
即sizeof(a)==sizeof(int*)

函数参数表中的数组实际上是指针
sizeof(a) == sizeof(int *)
但是可以用数组的运算符[ ]进行运算

以下四种函数原型是等价的:
int sum(int *a,int n);
int sum(int *,int );
int sum(int a[],int n);//作为参数表出现的话与第一个*a是等价的
int sum(int [],int );

总结:
sizeof(数组名):返回数组所有元素占有的内存空间字节数。
sizeof(指针) :返回计算机系统的地址字节数,如果是32位系统,返回4,16位系统返回2。

5.&边上必须是明确的变量

&(a+b)不可取(错误格式)
内存是堆栈分配,自小变大的,比如int类型
int a[10];
a[0]0x000004与a[1]0x000008相差4

6. int* p,q; 与int *p,q;表达意思一样

其中p(星p)为指针,q为int型变量
指针变量不能放实际值,只放别的(整型)变量的地址

7.数组变量是特殊的指针

(1)数组变量本身表达地址,所以

int a[10];
int *p=a;//不需要用&取地址
数组的单元表达的是变量需要用&取地址
如: a==&a[0]

(2) [ ]运算符可以对数组做,也可以对指针做

p[0]<==>a[0]

{
int *p=&min;//min为该数组最小值地址(由小到大排列)
*p==p[0]//两者值相等
}

(3)访问那个地址上的变量*

在C 语言中*号有三个用途,分别是:

乘号,用做乘法运算,例如5*6。
申明一个指针,在定义指针变量时使用,例如int *p;。
间接运算符,取得指针所指向的内存中的值,例如printf(“%d”,*p);。

*是一个单目运算符,用来访问指针的值所表示的地址上的变量
*运算符可以对指针做,也可以对数组做:
(表示一个指针数值)
*a=25;//可以读、写值

(4)数组变量是const(常量)的指针,所以不能被赋值

如:int b[ ]; b=a;或者int b[ ]=a;都是行不通的
数组变量之间是不能直接赋值或相互赋值的

int b[] <==>int * const b//常数不能被赋值
int *q=a;  //可以赋值

(5)作为参数的指针

void f(int *p);
int i=0;//在被调用的时候得到了某个变量的地址
f(&i);//在函数里面可以通过这个指针访问外面的这个i;

eg1
int a[] = {5, 15, 34, 54, 14, 2, 52, 72};
int *p = &a[1];
p[2]的值是54
注:由0开始,从左往右,则a[1]=15;
即p[0]=15;往右从零开始数2位得出p[2]=54
eg2
int a[] = {5, 15, 34, 54, 14, 2, 52, 72};
int *p = &a[5];
p[-2]的值是54 
注:由0开始,从左往右,则a[5]=2;
即p[0]=2;往左从零开始数2位得出p[-2]=54
eg3
int a[] = {0};
int *p = a;
注:其中p == &a[0]*p == a[0]与p[0] == a[0]表达式结果均为真

8.字符计算

‘a’=97 ~ ‘z’=122
‘A’=65 ~ ‘Z’=90

‘a’-'A’可以得到两段之间的距离(32)
a+ ‘a’-'A’可以把一个大写字母变成小写字母(+32)
a+ ‘A’-'a’可以把一个小写字母变成大写字母(-32)

9.逃逸字符(转义字符、逃脱字符)

定义:用来表达无法印出来的控制字符或特殊字符,它由一个反斜杠“\”开头,
后面跟上另一个字符,这两个字符合起来,组成了一个字符

字符意义
\b回退一格
\t到下一个表格位
\n换行
\r回车
\’单引号
\"双引号
\\反斜杠本身

注:
1." \t "不代表固定的字符的数量,而代表每行输出的固定位置
一个 \t 使输出从下一个制表位开始,可使上下两行对齐
2.回车换行是两个特殊的逃逸字符,源自打字机的动作
特别注意:在C语言中 \r 一般用不到, \n 表示回车换行两个动作

10.字符串

char word[]={'H','e','l','l','o','!'};
//是字符数组,不是C语言的字符串,因为不能用字符串的方式进行运算
char word[]={'H','e','l','l','o','!','\0'};<==>
char word[]={'H','e','l','l','o','!',0};
//结尾为0,便成了字符串
(1)以0(整数0)结尾的一串字符
(0或’\0’为16进制的0X30,即为10进制的48,但是和字符’0’不同)
(2)0标志字符串的结束,但它不是字符串的一部分
(计算字符串长度的时候不包含这个0)
(3)字符串以数组的形式存在,以数组或指针的形式访问(更多的是以指针的形式)
(4)string.h里有很多处理字符串的函数

定义字符串变量

//结尾自动补0
char *str="Hello";
char word[ ]="Hello";
char line[10]="Hello";

字符串常量
“Hello”
"Hello"会被编译器变成一个字符数组放在某处,这个数组的长度是6,结尾还有表示结束的0
两个相邻的字符串常量会被自动连接起来
如:在这里插入图片描述

总结

1)C语言的字符串是以字符数组的形态存在的
则不能用运算符对字符串做运算
通过数组的方式可以遍历字符串
2)唯一特殊的地方是字符串字面量,可以用来初始化字符数组,表现出C语言懂这个东西(字符串)
以及标准库提供了一系列字符串函数

注: 字符串字面量(string literal)是指双引号引住的一系列字符,双引号中可以没有字符,可以只有一个字符,也可以有很多个字符。

11.字符串常量

char *s = "Hello,World";
s是一个指针,初始化为指向一个字符串常量
由于这个常量所在的地方,所以实际上sconst char * s,但是由于历史的原因,编译器接受不带const的写法
但是试图对s所指的字符串做写入会导致严重的后果
如果需要修改字符串,应该用数组:
char s[] = "Hello,world!";

eg
#include<stdio.h>
int main()
{

	int i=0;
	char *s = "Hello World";//指针,要指向某个地方的字符串
	//s[0]='B';
	char *s2 = "Hello World";
	char s3[] = "Hello World";//数组,字符串就在我这里
	printf("&i=%p\t &i=%#x\n", &i, &i);
	/*证明本地变量和s*所指的变量不在一个地方,相差很远*/

	printf("s=%p\t s=%#x\n",s,s);
	printf("s2=%p\t s2=%#x\n",s2,s2);
	printf("Here!s[0]=%c\n", s[0]);
/*指针s,s2指向实际存放"Hello World"的地方,在只读代码段,
如s[0]='B'操作系统会起保护机制,会使程序崩溃*/
	printf("s3=%p\t s3=%#x\n", s3, s3);
	//在一个很大的地方,和本地变量一样,说明在本地变量那里
	s3[0] = 'B';
	printf("Here!s3[0]=%c\n", s3[0]);
/*把不可写的代码内容,拷贝到s3里面去,此时就可以修改字符串*/


	return 0;
}

运行截图:
在这里插入图片描述

问:字符串到底应该写成指针还是数组?

答:
char *str = "Hello";
char word[] = "Hello";
数组:这个字符串在这里

作为本地变量,则空间自动被回收

指针:这个字符串不知道在哪里

1)处理参数(只读不写)
2)如果数组作为函数的参数,实际上和指针是一样的,用指针来表达函数的参数,结果进来的都是指针
3)动态分配空间(用指针)

总结

如果要构造一个字符串数组
如果要处理一个字符串指针

注:char*是字符串这个说法不对(不确切)
字符串可以表达为char*的形式
char*不一定是字符串(比如,char*表示指针指向的是字节,或一串连续的字节,)

1)本意是指向字符的指针,可能指向的是字符的数组(就像int*一样)
2)只有它所指的字符数组有结尾的0,才能说它所指的是字符串

12.字符串计算

char *t ="title";
char *s;
s = t;

(1)并没有产生新的字符串,只是让指针s指向了t所指的字符串,对s的任何操作就是对t做的

char string[8];
scanf("%s",string);
printf("%s",string);
scanf读入一个单词(到空格、tab或回车为止)
注:scanf是不安全的,因为不知道要读入的内容的长度

(2)安全的输入

scanf("%7s",string);

//告诉scanf最多只能读取7个字符(可以不读满)

(3)在%和s之间的数字表示最多允许读入的字符的数量,这个数字应该比数组的大小小一,下面的内容(超过这个数字之后的数)会交给下一个%s或其他的scanf来阅读。

(4)常见错误

char *string;/没有初始化
scanf("%s",string);
以为char*是字符串类型,定义了一个字符串类型的变量string就可以直接使用了

由于没有对string初始化为0,所以不一定每次运行都出错(出现在本机上运行无错,拷贝到别人电脑上运行出错的情况)

(5)空字符串

char buffer[100]="";
//是一个空的字符串,是有效的字符串

这是一个空的字符串,buffer[0] == '\0'

char buffer[]="";

这个数组的长度只有1!只有buffer[0]是有效的,即 buffer[0] == '\0',而buffer[1] 就没有了,所以这个buffer里放不下任何字符串

13.字符串函数string.h

strlen

size_t strlen(const char *s);
//此处const为保证不会修改字符串
返回s的字符串长度(不包括结尾的 \0 )

代码:

#include<stdio.h>
#include<string.h>

size_t mylen(const char* s)
{
   int idx = 0;
   while(s[idx] != '\0')  {
   idx++;
}
return idx;
}

int main(int argc, char const *argv[])
{
  char line[]="Hello";
  printf("strlen=%lu\n",mylen(line));
  printf("sizeof=%lu\n",sizeof(line));
  return 0;
}

运行截图:
在这里插入图片描述

strcmp

int strcmp(const char *s1,const char *s2);
比较两个字符串(并比较出谁大谁小),返回:

0: 即s1 == s2
1: 即 s1 > s2 (并给出差值,如32)
-1: 即s1 < s2 (并给出差值,如-32)

代码:

#include<stdio.h>
#include<string.h>

size_t mycmp(const char* s1,const char* s2)
{
//数组法:
 //  int idx = 0;
//   while(s1[idx]  == s2[idx] && s1[idx]  != '\0')  {
//    idx++;
}
//return s1[idx] -s2[idx] ;
//指针法
while( *s1 ==*s2 && *s1 != '\0'){
   s1++;
   s2++;
}
return *s1 - *s2;
}

int main(int argc, char const *argv[])
{
  char s1[]="abc";
  char s2[]="Abc";
  printf("%d\n",mycmp(s1,s2));
  printf("%d\n",'a'-'A');
  return 0;
}

运行截图:
在这里插入图片描述

strcpy

char *strcpy(char *restrict dst,const char *restrict src);
把第二个参数src里的字符串拷贝到第一个参数dst所表达的的空间里面去

restrict表明src和dst不重叠,(C99)

最后会返回dst

为了能连起代码来(结果能参与运算)

复制一个字符串

char *dst=(char*)malloc(strlen(src) + 1);
strcpy(dst,src);

代码:

#include<stdio.h>
#include<string.h>

char* mycpy( char* dst, const char* src)
{
	//数组法
	//int idx = 0;
	//while (src[idx]) {
	//	dst[idx] = src[idx];
	//	idx++;
	//}
	//dst[idx] = '\0';
	//return dst;
	//指针法
	char* ret = dst;//最初dst的位置记录为ret,以便返回

	while (*src != '\0') {
		*dst = *src;
		dst++;
		src++;
	}

	//这个空循环与上面while函数代码等价(笔者不推荐萌新使用)
	//while(*dst++ = *src++)
	//    ;
	
	*dst = '\0';
//循环里与src对比到最后一位\0时会退出,此时src最后一位\0需要手动赋值给dst
	return ret;
}

int main(int argc, char const *argv[])
{
	char s1[] = "abc";
	char s2[] = "EDF";
	printf("s1=%s\ts2=%s\n", s1, s2);
	mycpy(s1, s2);
	printf("s1=%s\ts2=%s\n", s1, s2);

	return 0;
}

运行截图:
在这里插入图片描述

strcat

char *strcat(char *restrict s1,const char *restrict s2);
把s2拷贝到s1的后面,接成一个长的字符串
返回s1
s1必须具有足够的空间

安全问题

strcpy和strcat都可能出现安全问题

如果目的地没有足够的空间,越界了也不知道

安全版本

char *strncpy(char *restrict dst,const char *restrict src,size_t n);
限定多少字符,多了掐掉,安全的不会越界
char *strncat(char *restrict s1,const char *restrict s2,size_t n);

int strncmp(const char *s1,const char *s2,size_t n);
不是为了安全,只判断前n个

字符串中找字符

char *strchr(const char *s,int c);
strchr() 函数会依次检索字符串 s 中的每一个字符,直到遇见字符 c(一个无符号字符),或者到达字符串末尾(遇见\0)。
从左往右,在字符串中寻找c第一次出现的位置,返回的是指针
char *strrchr(const char *s,int c);
strrchr() 函数查找字符串在另一个字符串中最后一次出现的位置,并返回从该位置到字符串结尾的所有字符。
从右往左,在字符串中寻找c第一次出现的位置,返回的是指针
返回NULL表示没找到

示例代码:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>

int main(int argc, char const *argv[])
{
	char s[] = "hello";
	char *p = strchr(s,'l');//第一个l及后面的数组
	printf("第一个l及后面的代码=%s\n", p);
	char *p1 = strchr(p+1, 'l');//第一个l及后面的数组
	printf("第二个l及后面的代码=%s\n", p1);

	char *t = (char*)malloc(strlen(p) + 1);//动态分配内存
	strcpy_s(t, strlen(p)+1, p);	
	//将p中字符串复制到t,最后一个空间为'\0'结束符
	printf("目标字符后面的代码=%s\n",t);
	free(t);//记得释放空间!!!
	
   //小技巧:取指针指向位置前面的字符串
	char c = *p;
	*p = '\0';
	//此时数组s里的字符串只剩下指针p指向位置前面的字符串
	
	printf("%c\n",*p);
	char *t1 = (char*)malloc(strlen(s) + 1);
	strcpy_s(t1, strlen(s) + 1, s);
	//将s中字符串复制到t1
	printf("目标字符前面的代码=%s\n", t1);
	*p = c; //使用完后恢复
	printf("%c\n", *p);
	free(t1);

	return 0;
}

运行截图:
在这里插入图片描述
小技巧原理图解(记得改完要恢复函数!!!)
在这里插入图片描述

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吃鱼从来不吐刺

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值