C语言——基础笔记

最近更新时间:11.3

预处理

#incldue<stdio.h>
#define me chen
#define pi 3.1415926
const int pi=3.1415926

#incldue,#define是预处理指令,简单说下define,就是把后面的用前面的替换,通常用来定义不变的常量,也可以用关键字const,也称明示常量,关键字作用是保护变量的值不被改变

转义符

\n//换行
\a//警报
\b//退格
\f//换页
\r//回车
\t//水平制表符
\v//垂直制表符
\\//反斜杠
\'//单引号
\"//双引号

数据类型

最小存储单位是位(bit)
字节是常用的计算机存储单位,一字节均为八位,有例外
字是设计计算机时给定的自然存储单位
按照存储方式不同分为两种整数浮点数

//基本数据类型
int
long
short//范围比int小
unsigned//用于非负
char
float//浮点型,通常存储要32位
double//双精度,通常64位
_Bool//布尔值true/false也就是0/1
#include<stdio.h>
#include<stdbool.h>//使用_Bool要此添加头文件
int main(void){
	_Bool a=false;
	if(a){
		printf("a");
	}else{
		printf("b");
	}
	return 1;
}

输出转换说明:

%d//int类型
%.2f//float和double,.2表示输出小数点后两位
%c//char类型
%e//打印指数计数法的float
%s//字符串
%p//打印地址

数组

由一系列相同类型元素组成,也可以用const来把数组变为只读
数组声明

int a[89];//声明是必须指定长度,中括号内即为长度
char b[23];
float c[23];

并且数组是从序号0开始的,即a[0]为第一个数,最后一个数的序号是声明时括号内的数字减一。越界报错!

//数组初始化
int a[3]={1,2,3};
//使用前必须初始化,且初始化的数量不能大于数组长度
int a[]={[0]=6};
//可以指定下标直接赋值
//未赋值的会设置为零

多维数组

二维数组

int a[3][4];//相当于矩阵,三行四列,第一个数是行,第二个为列
int a[3][4]={{1,2,3,4},{1,2,3,4},{1,2,3,4}}
int a[3][4]={{1,2,3,4},
             {1,2,3,4},
             {1,2,3,4}}//这样看第一个数的序号为a[0][0],第二行第三列序号为a[1][2];

三维,多维均可类推。

对于二维数组

a[1];//这个可以表示一维数组,也可以带入函数当中去运算

三位数组则这个代表其中一个二维数组。

数组带入函数参数的形式:

add(int a[],int length);//后面的length指的是数组长度
add(int a[][7],int length);//二位数组第二个中括号里面必须有对应的长度,否则无效。

scanf()与printf()

格式化输入输出

//格式
a=scanf("%d",&num);
printf("%d %d",a,num);

这两个语句结果num是你输入的值,如果你输入的是%d对应的类型的值,则scanf函数会返回1给a,不合法则返回0。可以利用这个特性,并且传的值在num变量内

字符

专门对字符进行输入输出的函数:getchar()和putchar()

//这里说的是字符,并不是字符串。每次输入一个字符
char ch=getchar();
putchar(ch);

还有一些对字符操作的函数,在ctype.h的头文件内

//熟记
isalpha()//判断是否为字母,是返回真
isdigit()//判断是否为数字,是返回真
islower()//判断是否为小写字母,是返回真
isupper()//判断是否为大写字母,是返回真
isspce()//判断是否为空格(换行符,换页符等),是返回真
ispunct()//判断是否为标点符号(除空格,字母数字以外的可打印字符),是返回真
#include<stdio.h>
#include<ctype.h> 
#include<stdbool.h>
int main(void){
	char ch;
	while((ch=getchar())!='\n'){
		if(isalpha(ch)) {
			printf("1");
		}
		if(isdigit(ch)) {
			printf("2");
		}
	}
	return 1;
}

字符串

是一个或多个字符的序列,C没有专门存储字符串的变量,只能用字符数组存储,并且字符串最后有一个’\0’空字符来表示字符串的结束

char a[20];//实际上只有19个字符,最后一个为空字符

strlen和sizeof

都是计算字符串长度的

#include<stdio.h>
#include<string.h>//用strlen函数要加的
#define chen "sdhsidioa" 
int main(void){
	int size=sizeof(chen);//sizeof把最后一个空字符也算入了
	int s=strlen(chen);//这个函数并没有
	printf("%d %d",size,s);
	return 0;
}

结果

10 9

运算符

加减乘除

+,-,*,/
//整数除会去除小数部分,不会四舍五入
%//取余运算
15%4//结果为3,取余数
//对于有负数的取余,第一个是负数,结果就为负数

指数运算需要pow()函数,需要头文件math.h

int a=pow(2,3)//2的3次方

++和- -

自加和自减运算符,优先级仅次于括号

a++;//相当于下面语句
a=a+1;

有两种形式

int a=2;
x=2*a++;//先使用a然后对a进行自加
y=2*++a;//先对a自加,再使用a的值

不同的运算符

a+=1 等同于 a=a+1;//加减乘除,取余都可以

逗号运算符

,//被他分割的表达式从左往右运算
a=9;
a=a+1,b=a-1;

a=10,b=9

强类型转换

x=(int)9.87;//会把9.87变成整型9,其他的亦如此

也有自动类型转换,只会往高级转,例如float会在函数参数中升级为double类型

逻辑运算符

&&//与--两边的表达式为真,则为真
||//或--两边一个为真,则为真
!//非--相反,优先级仅低于括号

在有逻辑运算符的表达式中运算顺序是从左往右

while(a++<3&&x<4)//在这里面,在右边的表达式比较前,a会自加

条件运算符

也是一个三元运算符

int x=(y<0)?1:0;//意思是x的值取决于y,当y小于0时,x的值为冒号前面的数,不小于0反之

&运算符

&运算符给出变量存储地址,地址即为内存中的位置

int main() {
    int ch=1;
    printf("%p\n",&ch);//%p输出地址的转换说明
    return 0;
}

循环

每次循环称为一次迭代
在判断语句中,0表示假,非零数都表示真
并且要注意==和=的区别,前一个是判断语句,后一个是赋值语句

优先级

关系运算符比算术运算符优先级低

if(a == b+1)//这里要先对b进行运算,再比较

while循环

while(条件){
    //执行语句,当执行语句只有一条可省略大括号
}
while(a<3){
		a++;
	}

for循环

int i;
//最基本的for循环
for(i=0;i<10;i++){
		printf(a);
	}
//递减
for(i=8;i>0;i--)
//改变条件
for(i=2;i<89;i=i+22)
for(i=2;i*i<89;i=i+22)
//总之可以灵活运用,也可以省略其中某个条件
i=9;
for(printf("a");i--)//亦可
for(i=0,j=0;j<8,i<9;j++,i++;)

do while循环

前面两个都是入口条件循环,即进入需要满足条件,而do while是出口条件循环。也就保证了至少一次的执行。然后再进行判断,不符合条件退出循环。

do{
a++;
}while(a!=1);

可以用循环给数组赋值

for(i=0;i<10;i++){
		scanf("%d",&a[i]);
	}
while(i<9){
	scanf("%d",&a[i]);
}

控制语句

if else

if(a){
	printf("a");
}else{
	printf("b");
}
//else if可以多层嵌套
if(a){
	printf("a");
}else if(a){
	printf("b");
...
}else {
	printf("c");
}

循环辅助

continue//跳出当前循环,进入下一次循环
break//跳出循环
for(i=0;i<10;i++){
	if(i==7) continue; 
	printf("%d",i);
}
printf("\n");
for(i=0;i<10;i++){
	if(i==7) break; 
	printf("%d",i);
}

输出

012345689
0123456
int i=0;
	while(i<10){
		int j=0;
		while(j<8){
			i++;
			j++;
			printf("%d %d",i,j);
			break;//此处跳出内部while循环
		}
	}

switch

对switch后面括号内表达式求值,再与下面的标签进行匹配,如果没有匹配的,执行默认的语句,如果没有默认的则不执行,接着运行下一个语句

char ch=getchar();
switch(ch){
	case 'a': printf("chen") ;break;//每一句都要加上break,如果没有则会在匹配相应的标签后执行标签以及标签后面的所有标签语句,在这里break作用跳出switch
	case 'b': printf("luo");break;
	default: printf(" ") ;break;
}
...

指针

是一个值为内存地址的变量

//声明
int * p;//*和p之间的空格可有可无
char * p1;
* //是间接运算符,也叫解引用运算符,与乘法不同

可以这么理解:p是指向一个地址的,他的值是地址,加上*运算符后,把地址解析成对应类型的值要注意的是指针是一个新的类型,虽然地址的值输出是一个整型,但不能把他当作整型来操作

#include <stdio.h>
void add(int *a);
int main() {
    int num=0;
    add(&num);//调用时要把地址传入进去
    printf("%d",num);
    return 0;
}
void add(int *a){
    (*a)++;//注意带有*运算符时,要使用括号,否则可能有误
}

指针与数组

int temp[12];
int *p=temp;

在数组中,数组名代表数组的地址,也可以用&运算符来取得数组地址

int address=&temp[0]//得到数组的地址

用指针遍历数组

#include <stdio.h>
#define size 5
int main() {
    int a[size]={1,2,3,4,5};
    int * p=a;
    int i = 0;
    for (; i < size; ++i) {
        printf("%d\n",*(p+i));
    }
    return 0;
}

注意:如果这里循环输出地址,可以看到地址是按照一定数量增加的,这是因为指针加一,是指向下一个元素的地址,而不是地址加一,虽然指针指向的是地址,但地址加的是定义类型占用的字节数,不同类型占用的字节数不一样,需要注意

对于函数的参数,用数组初始地址的指针形式带入,可以替换,数组型的参数,例如:

int add(int *p,int *length) {//p指向的是数组第一个元素的位置
	int count=0;
	while(p<length){
		count+=*p;
		p++;
	}
	return count;
}
int Add(int a[],int length){
	int i,count=0;
	for(i=0;i<length;i++){
		count+=a[i];
	}
	return count;
}
int cou[]={1,2,3,4,5,6,7,8,9,10};
    int length=10;
    int *p=cou;
    int one=add(p,p+length);//p+length是最后一个元素的后一个位置
    int two=Add(cou,length);
    printf("%d\n",one);
    printf("%d",two);
    return 0;

这里的相加是指针运算的一种。有下面集中运算:

  • 赋值:把地址赋值给指针
  • 解引用:用*运算符取得指针指向地址的值
  • 取地址:用&运算符给出本身的地址
  • 与整数相加:把整数乘以指针指向类型的字节数,再加上指针的地址(减亦相同,递加递减也一样)
  • 指针相减:相减的意思为这两个指针之间隔着多少所指向类型的个数

注意:不能用*去解引用一个未初始化的指针

int *p;
*p=2;

这是有问题的,指针还未初始化,值是随机的,这样做并不知道2存储在哪,可能不会出错,也可能导致崩溃

指针和多维数组

int cou[4][2];

cou是数组的地址,cou和&cou[0]相等,cou[0]本身是一个含两个整数的数组,cou[0]的值和第一个元素的地址相同,即&cou[0][0];cou+1和cou[0]+1不同,第一个指向的对象有两个元素,cou[0]指向的对象只占用一个元素。所以cou指的是cou[0],(cou[0])指向的是cou[0][0],*cou对cou两次解引用,等于(cou[0])。

#include <stdio.h>
int main() {
    int cou[4][2]={{2,3},{4,5},{6,7},{8,9}};
    //等价写法
    printf("%d\n",cou[0][0]);
    printf("%d\n",*cou[0]);
    printf("%d\n",**cou);
    return 0;
}

用指针指向二维数组

#include <stdio.h>
int main() {
    int cou[4][2]={{2,3},{4,5},{6,7},{8,9}};
    int (*p)[2];//加括号因为[]的优先级高于*,会先合p结合
    p=cou;
    printf("%d\n",cou[0][0]);
    printf("%d\n",*p[0]);
    printf("%d\n",**p);
    return 0;
}
*p[2];//这表示指针数组
(*p)[2];//这是二维数组,*p指向的是cou[0]
**p;//指向指针的指针

字符串和字符串函数

这里写的不放在前面是因为涉及指针。

定义字符串

用双引号括起来的内容称为字符串字面量,也叫字符串常量,会在后面自动加入’\0’。如果要在字符串内部使用双引号,要在前面加上反斜杠。

char in[10];
in="\"Y\"";//输出“Y”

用双引号括起来的内容被视为指向该字符串存储位置的指针

//看个示例
printf("%s %p %c\n","chen","chen",*"chen");
//输出
chen 0000000000404000 c//字符串,地址,字符串首个字符

字符串数组初始化时需要确定数组大小,也可以让编译器来计算大小,只不过只能用在字符串数组初始化时,如果要先定义一个字符串数组,不进行初始化,则必须确定大小。

char in[10];
char in[]="zifuchuan";//计算大小交给编译器
char *p="zifuchuan";//用指针表示法来
数组与指针的不同

字符串存储在静态存储区,在程序运行时会给数组分配内存,并把字符串拷贝给数组。而指针则是指向字符串的首位地址,是把字符串地址拷贝给指针,并且数组内的字符串可以修改,而指针指向的一般不行,修改后会出现问题,这就隐喻的表示可以对数组名进行*p=in+1的一个赋值,而不能对数组名自加,自加相当于改变数组的地址,前一步只是把对应地址赋值给指针;相对应的指针对字符串数组的定义则可以进行指针自加,即指向下一个字符的地址
数组名不是变量,数组的元素是变量,指针也是变量

//对于上面的初始化代码
in=p//不合法
p=in//合法

因为指针定义的字符串数组最好别修改字符,可以用const限定符来定义

const char *p="hello world";
二维字符串数组

定义方式如下,可以直接初始化,也可以之后初始化

	char *temp[3]={
		"apple";
		"banana";
		"couple";
	} 
	char temp1[3][10]={
		"buy";
		"chair";
		"chip";
	}
temp[0]//指的是第一个字符串数组
temp[0][0]//指的是第一个字符串数组中的第一个字符
temp1[0]//指的也是第一个字符串数组
temp1[0][0]//指的是第一个字符串数组中的第一个字符

不同之处是用指针定义的字符串二维数组,他的temp[0]是不定长的,而另一个时指定长度的。 有空间的利用效率的提高

字符串I\O

输入函数
gets()//读取整行输入,直至遇到换行符,然后丢弃换行符,储存输入字符,并在结尾加一个空字符,通常和puts()一起用
char temp[81];
gets(temp);
puts(temp);

存在一个问题:可能输入的字符会超出,导致溢出,可能会擦写掉其他数据,之后用其他函数替换

fgets(A,B,C)
//第一个参数是字符串变量
//通过第二个参数来限制输入的字符数,读取n-1个,或者读到换行符结束
//读到换行符会存入
//第三个参数是读入的文件,如果从键盘读入则以stdin作为参数
//通常与fputs()一起使用
	char in[10];
	puts("enter:\n");
	while(fgets(in,10,stdin)!=NULL&&in[0]!='\n'){//如果读到文件末尾,将返回一个特殊指针--空指针,可以用0代替,不够用宏NULL更常见。
		fputs(in,stdout);
	}
	printf("over");
gets_s()
//只从标准输入中读取数据,不需要第三个参数
//遇到换行符会舍弃
//如果gets_s()读到最大字符数都没有读到换行符,会执行以下几步。首先把目标数组中的首字符设置为空字符,读取并丢弃随后的输入直到读到换行符或文件结尾,然后返回空指针。接着,调用依赖实现的处理函数(或者你选择的其他函数),可能会中止或退出程序

对于上面三个,相比最好的是fgets()

输出函数
puts()//输出字符串,并在结尾添加换行符,里面的参数是字符串的地址
	char temp[]={"why do you do that?"};
	char *temp1={"why do you do that?"};
	
	puts(temp);
	puts(temp1);
	puts(&temp[4]);//从该地址输出直到字符串末尾
	puts(temp+3);//同上

在这里插入图片描述

fputs()//同fgets()有三个参数
//如果打印到显示器上则以stdout作为参数
//不会在输出的末尾添加换行符

建议使用fputs()

字符串函数

这些函数原型放在string.h头文件内

函数作用
strlen()统计字符串长度(上面说过)
strcat()拼接字符串,把后面的的加到前面,并返回第一个的地址
strncat()拼接字符串,第三个参数限制拼接的最大添加字符数
strcmp()比较两个字符串是否相等,参数为地址,相同返回0,输出依据ASCII码来,如果第一个在第二个前面,返回负数,否之返回正数
strncmp()比较两个字符串是否相等,第三个参数限制比较的大小,从第一个开始计数
strcpy()拷贝字符串,把第二个参数拷贝到第一个参数里面,第二个参数可以是指针,变量,常量,第一个参数可以不用不用从第一个位置开始,也就是复制的位置可以放在字符串的任意位置,返回的是第二个参数地址
strncpy()拷贝字符串,第三个参数限制拷贝的大小
sprintf()和printf用法一样,把数据写入字符串,并且多一个参数,把字符串写入参数内
strlen(a);
strcat(a,b);
strncat(a,b,c);
strcmp(a,b);
strncmp(a,b,c);
strcpy(a,b);
strncpy(a,b,c);
sprintf(a,"%s",b);
#include<stdio.h>
#include<string.h>
int main() {
	char temp[100];
	char p[100];
	gets(temp);
	const char *a="add";
	int size=strlen(temp);
	printf("字符串长度为:%d\n",size);
	puts(strcat(temp,a));
	if(strcmp(temp,a)==0)
		printf("字符串相等!\n");
	else
		printf("字符串不等!\n");
	puts(strcpy(temp+size+3,a));
	puts(temp);
	
	sprintf(p,"%s is now!\n",temp);
	puts(p);
    return 0;
}

在这里插入图片描述

文件I/O

打开文件

使用 fopen( ) 函数来创建一个新的文件或者打开一个已有的文件,这个调用会初始化类型 FILE 的一个对象,类型 FILE 包含了所有用来控制流的必要的信息。

//函数调用原型
FILE *fopen( const char *filename, const char *mode );

filename 是字符串,用来命名文件,访问模式 mode 的值可以是下列值中的一个:

模式描述
r打开一个已有的文本文件,允许读取文件
w打开一个文本文件,允许写入文件。如果文件不存在,则会创建一个新文件。在这里,您的程序会从文件的开头写入内容。如果文件存在,则该会被截断为零长度,重新写入
a打开一个文本文件,以追加模式写入文件。如果文件不存在,则会创建一个新文件。在这里,您的程序会在已有的文件内容中追加内容
r+打开一个文本文件,允许读写文件
w+打开一个文本文件,允许读写文件。如果文件已存在,则文件会被截断为零长度,如果文件不存在,则会创建一个新文件
a+打开一个文本文件,允许读写文件。如果文件不存在,则会创建一个新文件。读取会从文件的开头开始,写入则只能是追加模式
//如果处理的是二进制文件,则需使用下面的访问模式来取代上面的访问模式
"rb", "wb", "ab", "rb+", "r+b", "wb+", "w+b", "ab+", "a+b"

关闭文件

//如果成功关闭文件,fclose( ) 函数返回零,如果关闭文件时发生错误,函数返回 EOF。这个函数实际上,会清空缓冲区中的数据,关闭文件,并释放用于该文件的所有内存。EOF 是一个定义在头文件 stdio.h 中的常量
int fclose(FILE *fp);

写入文件

//下面是把字符写入到流中的最简单的函数
int fputc( int c, FILE *fp );
//函数 fputc() 把参数 c 的字符值写入到 fp 所指向的输出流中。如果写入成功,它会返回写入的字符,如果发生错误,则会返回 EOF。您可以使用下面的函数来把一个以 null 结尾的字符串写入到流中
int fputs( const char *s, FILE *fp );
//函数 fputs() 把字符串 s 写入到 fp 所指向的输出流中。如果写入成功,它会返回一个非负值,如果发生错误,则会返回 EOF。
//也可以使用下面函数把一个字符串写入到文件中
int fprintf(FILE *fp,const char *format, ...) 

写入实例

#include<stdio.h> 

int main(){
	printf("文件IO");
	//定义文件操作指针变量
	FILE *fp = NULL;
	//打开文件,windows文件位置要用转义符双斜杠
	fp = fopen("E:\\Installation\\DevC++\\Workplace\\test.txt", "w+");
	//写入内容
	fprintf(fp, "写入成功!\n");
	fputs("另一种写入方式!\n", fp);
	fclose(fp);
	
	return 0;
}

读取文件

//下面是从文件读取单个字符的最简单的函数
//fgetc() 函数从 fp 所指向的输入文件中读取一个字符。返回值是读取的字符,如果发生错误则返回 EOF
int fgetc(File fp);
//下面的函数允许从流中读取一个字符串,函数 fgets() 从 fp 所指向的输入流中读取 n - 1 个字符。它会把读取的字符串复制到缓冲区 buf,并在最后追加一个 null 字符来终止字符串,如果这个函数在读取最后一个字符之前就遇到一个换行符 '\n' 或文件的末尾 EOF,则只会返回读取到的字符,包括换行符。
char *fgets( char *buf, int n, FILE *fp );
//也可以使用下面函数来从文件中读取字符串,但是在遇到第一个空格和换行符时,它会停止读取
int fscanf(FILE *fp, const char *format, ...) 

//实例1
#include<stdio.h> 

int main(){
	
	FILE *fp = NULL;
	char buffer[1000];
	fp = fopen("E:\\Installation\\DevC++\\Workplace\\test.txt", "r");	
	fgets(buffer, 1000, (FILE*)fp);
	printf("%s\n", buffer);
	fclose(fp);
	
	return 0;
}

//实例2
#include<stdio.h> 

int main(){
	
	FILE *fp = NULL;
	char buffer[1000];
	fp = fopen("E:\\Installation\\DevC++\\Workplace\\test.txt", "r");
	fscanf(fp, "%s", buffer);
	printf("%s\n", buffer);
	fclose(fp);
	
	return 0;
}

读取实例

#include<stdio.h> 

int main(){
	
	FILE *fp = NULL;
	char buffer[1000];
	fp = fopen("E:\\Installation\\DevC++\\Workplace\\test.txt", "r");
	fscanf(fp, "%s", buffer);
	printf("%s\n", buffer);
	
	fgets(buffer, 1000, (FILE*)fp);
	printf("%s\n", buffer);
	
	fgets(buffer, 1000, (FILE*)fp);
	printf("%s", buffer);
	fclose(fp);
	
	return 0;
}

//文件内容如下
写入 成功
另一种写入方式!
//输入如下
写入
 成功
另一种写入方式!
//fscanf碰到空格读取完毕,之后的fgets接着读取知道换行,最后一个fgets读取下一行的内容。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值