当前位置:网站首页>C语言学习笔记

C语言学习笔记

2022-07-06 09:25:00 编程小鱼六六六

数据

常量

大分类

  1. 字面常量: 直观写出来的值
  2. const修饰发常量
  3. #define定义的常量
  4. 枚举常量

常量,const常变量

int main()
{
    const int num = 1;\\const 表示常属性变量的创建
    num = 8;
    return 0;
}
​

是不能运行的

int main()
{
    const int n = 4;
    printf("%d\n", n);
    int arr[n] = { 0 };
    return 0;
}
​
int main()
{
    const const int n = 4;
    printf("%d\n", n);
    int arr[n] = { 0 };
    return 0;
}

都不能运行, const int n表示n是一个具有常属性的变量,不是常量.

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#define MAX 10
int main()
{
    int arr[MAX] = { 0 };
    return 0;
}

这个可以运行,因为#define定义了一个标准常量

枚举常量

enum Sex\\枚举常量
{
    MALE,
    FEMALE,
    SECRET
};//枚举常量的可能取值, 大括号内给定的枚举常量是不能改变的, 枚举变量是可以改变的
int main()
{
    printf("%d",MALE);//0
    printf("%d",FEMALE);//1
    printf("%d",SECRET);//2
}

字符串+转义字符+注释

字符串

多个字符放在双引号内就是字符串 "hello bit\n"

int main()
{
"abcdef";//字符串
"";//空字符串
return 0;
}

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
    char arr1[] = "abc";
    //"abc"--'a','b','c','\0' =>'\0'表示字符串的结束标志,'\0'的值是0
    //char arr2[] = { 'a','b','c' };'c'的后面应该加上一个0
    char arr2[]={'a','b','c',0};
    printf("%s\n", arr1);
    printf("%s\n", arr2);
    printf("%d\n", strlen(arr1));
    printf("%d\n", strlen(arr2));//打印字符串长度,3
    return 0;
}

数据在计算机上存储的是二进制,字符是以ASCII编码储存.

转义字符\

 \n把本义变了,使某一个字符不再是原来的意思,表示换行

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
    printf("abc\n");
    return 0;
}

\t叫水平制表符,类似Tab键

注释

c的注释/* */ c++的注释//` 都可以用 注释用于代码难懂的地方

输入输出

格式字符

格式符号作用
i, d十进制整数
x, X十六进制无符号整数
o八进制无符号整数
u无符号十进制整数
c单一字符
s字符串
e指数形式浮点小数
f小数形式浮点小数
#include<stdio.h>
int main()
{
    float f=3.1415926;
    printf("%2.3f\n",f);
    printf("%f\n",f);
    return 0;
}

其中%2.3f2表示格式(被超过了就忽略), 3表示保留小数点位数(四舍五入)

修饰符功能
m输出数据域宽,数据长度<m,左补空格;否则按实际输出
.n对实数,指定小数点后位数(四舍五入)
对字符串,指定实际输出位数
-输出数据在域内左对齐(缺省右对齐)
+指定在有符号数的正数前显示正号(+)
0输出数值时指定左面不使用的空位置自动填0
#在八进制和十六进制数前显示前导0,0x
l在d, o, x, u前,指定输出精度为long型
l在e, f, g前,指定输出精度为double型

字符输入函数

putchar()

scanf() 如果需要提示语句, 单独写一行printf, 不能写在scanf中

输入函数留下的垃圾

即输入一个整型值后紧接着按回车输入另一个字符, 会将回车变成输入. 清除的方法有

1. 用getchar()吃掉
1. %[空格]c或%*c

字符串输入函数

  1. gets(s)
  2. scanf()会将空格作为字符串输入的结束标志(处理连续字符输入时可以处理空格)

字符串输出函数

puts

会自动换行

运算符

算数运算符

加 减 乘 除(/) 取余(%) 取余不能给浮点数进行.

关系运算符

>``>=``<=``<

==``!=

逻辑运算符

  1. !取反: 1变 0,0变1
  2. ||

位运算符

针对二进制的位(0/1)

位运算可以以任何数据类型

运算符功能说明
~位逻辑反转换为二进制, 然后0 1 互换
&位逻辑与
|位逻辑或
^位逻辑亦或相异为真, 相同为假
>>右移位
<<左移位乘二

特殊运算符

赋值运算符

 

条件运算符?

逗号运算符

z=(x+=5,y=x+0.2)

z的值由最后一个表达式决定

运算符的优先级

建议多加括号解决优先级问题

#include<stdio.h>
int main()
{
	int x = 1, y = 0, z = 0;
	x += y == z,y = x + 2, z = x + y + x > 0;
	printf("%d,%d,%d\n", x, y, z);
	return 0;
}

补充

解引用运算符*的优先级要小于下标运算符[], 因此表示数组指针需要加括号例如int (*p)[10]

数组

一维数组

C语言中不会对数组做越界判断, 需要自己限制

  1. 数组如果不初始化, 值是随机的.
  2. static数组元素不赋值, 默认是0
  3. 只给部分赋值, 后面默认0
  4. 初始化给多了直接报错

例题: 冒泡排序

#include<stdio.h>
int main(int argc, char const *argv[])
{
	int a[7]={49,38,97,76,13,27,30};
		int i = 0,j=6,temp;
	for (;j>=0;j--)
	{
		for(i=0;i<=j;i++)
			{
				if(a[i]>a[i+1])
				{
					temp = a[i];
					a[i] = a[i+1];
					a[i+1] = temp;
				}
			}
			
	}
	for(i=0;i<=6;i++)
		{
			printf("%d\n",a[i]);
		}

	return 0;
}

二维数组

P[行][列]

定义方式: 行数可以省略, 列数不可以

二维数组的存储

二维数组存储的验证

#include<stdio.h>
int main(int argc, char const *argv[])
{
	int a[4][5]={
   {1,2,3,4,5},{6,7,8,9,10},{11,12,13,14,15},{16,17,18,19,20}};
	for(int x = 0; x < 4; x++)
	{
		for(int y = 0; y < 5; y++)
		{
			printf("The id of a[%d][%d] is %p\n",x,y,&a[x][y] );
		}
	}
	return 0;
}

多维数组

例题: 打印杨辉三角的前十行

#include<stdio.h>

int main(int argc, char const *argv[])
{
	int a[10][10];
	for(int x = 0; x < 10; x++)
		{
			for(int y = 0; y < 10; y++)
			{
				if(y == 0 ||x == y)
					a[x][y]=1;

			}
		}
	for(int x = 2; x < 10; x++)
	{
		for(int y = 1; y < x; y++)
		{
			a[x][y]=a[x-1][y-1]+a[x-1][y];
		}

	}
	for(int x = 0; x < 10; x++)
	{
		for(int y = 0; y <= x; y++)
		{
			printf("%d ",a[x][y]);
		}
		printf("\n");
	}
	return 0;
}

字符数组和字符串

C语言中, 字符串依附于字符数组存在

初始化

  1. 逐个元素赋值
  2. 字符串赋值 "abc" ->a, b, c, \0
    0 \0 NULL都是一回事, ASCII码值是0

, '0'是另一回事, ASCII码值是48

二维数组下的字符串数组

#include<stdio.h>
int main()
{
	char fruit[][20] = { "bannana","apple","strawmerry","watermelen" };
	int i, j, n, m;
	n = sizeof(fruit) / sizeof(fruit[0]);
	m = sizeof(fruit[0]) / sizeof(fruit[0][0]);

	for (i = 0; i < n; i++)
	{
		for (j = 0; j < m; j++)
		{
			putchar(fruit[i][j]);
		}
		putchar('\n');
	}
	return 0;
}

sizeof()得到的是总空间, 包括未赋值部分, strlen得到的是实际的字符串长度(需要string.h>)

字符串函数

常见字符串函数 <string.h>

strlen求长度

计算一个字符串的实际长度(不包含\0), 返回的是字节数, 往往一个char就是一个字节, 因此可以视为长度

char s[10] = { 'A', 0, 'b', '\0', '0' };
printf("%zd", strlen(s));//这将返回1, 因为遇到0, 就认为字符串结束了
printf("%d", sizeof(s)/sizeof(char));//这将返回10, 表示整个数组

遇到特殊字符时:

char s[20] = "\t\t\nasdczx0\0qwe";
printf("%zd\n", strlen(s));//返回10  \t,\n分别被视为一个字符
printf("%d", sizeof(s)/sizeof(char));//返回20
char s[20] = "\x69\141\n"; // \xhh表示十六进制数代表的符号, \ddd表示八进制数代表的符号 一般不建议这样写
printf("%zd\n", strlen(s));

strcpy字符串拷贝

  1. strcpy(字符数组1,字符数组2)
  2. 将字符串2拷贝到字符串1中去, 返回字符数组1的首地址.(必须是字符串)
  3. 字符数组1必须足够大

strcat字符串连接函数

  1. strcpy(字符数组1,字符数组2)
  2. 将字符串2连接到字符串1后面, 返回字符数组1的首地址.(必须是字符串)
  3. 字符数组1必须足够大
  4. 连接之前,二者都是以'\0'结束的, 连接后 串1的'\0取消,新串最后加'\0'

strcmp字符串比较函数

strcmp(字符串1,字符串2)

  1. 比较规则: 两个字符串从左到右依次比较ASCII码值, 若1<2 返回负整数, 若1==2返回0, 若1>2返回正整数.
  2. 如果长度不等, 空的位置相当于和\0比 ,

注意: 字符串函数的使用前提必须是字符串或带'\0'的字符数组, 否则将出现不可以预知的问题.

字符串函数的扩展用法

strncpy(p,p1,n)复制指定长度的字符串

strncat(p,p1,n)连接指定长度的字符串

strcasecmp忽略大小写比较字符串

strncmp(p, p1, n) 比较指定长度字符串

strchr(p, c) 在字符串中查找指定字符

#include<stdio.h>

#include<string.h>
int main(int argc, char const* argv[])
{
	char s1[]="123aeqw$asdsa";
	int ch;

	ch='$';

	printf("%p,%p,\n\n",s1,strchr(s1,ch));	
	return 0;
}

strstr(p, p1) 查找字符串

#include<stdio.h>
#include<string.h>
int main(int argc, char const* argv[])
{
	char s1[]="how are you";
	char s2[]="are";
	printf("%ld\n",strstr(s1,s2)-s1);
	return 0;
}

关于字符的函数, 需要#include<ctype.h>

isalpha() 检查是否为字母字符

isupper()检查是否为大写字母字符

islower() 检查是否为小写字母字符

isdigit() 检查是否为数字

指针

在C语言中, 内存单元的地址叫指针. 在不影响理解的情况下, 地址, 指针, 指针变量不做区分.

指针运算

  1. 只限于指向相同类型的指针
  2. 指针相减得到的是所隔数据的个数, 不是地址差.

赋值

可以将地址赋值给指针.

解引用

*运算符给出指针指向地址上的存储的值

取址

和所有变量一样,指针变量也有自己的地址和值. &运算符给出指针本身的地址.

指针与整数相加

整数都会和指针指向类型的大小相乘, 再把结果与初始地址相加.

递增指针

递增指向数组元素的指针可以让该指针移动到数组的下一个元素. 因此ptr1指向urn[1].

指针减去一个整数

指针必须是被减数. 其余与加法类似.

递减指针

类似递增, 用于数组

指针求差

指针与数组

数组名是指向数组第一个元素的指针.

二维数组中的指针

int a[3][2]={
   {1,6},{9,12},{61,12}};
int *p,i,n;

n=sizeof(a)/sizeof(int);
p=a;//warning

这样写是不对的, a与p类型是有区别的, 一个p指向四个字节, 但是一个a指向8个字节, 应该这样:

p=a[0]

也就是说 如果需要用一级指针遍历二维数组, 需要for循环

二维数组由多个数组构成,

上述代码中a就是由a[0] a[1] a[2] 三个数组构成.

对二维数组名进行一次解引用将得到指向一行数的指针.

字符指针与字符串

如果直接让指针指向字符串

char *p="hello world";

指针存储了字符串常量的起始地址, 不能用指针修改 指向对象的值了. 对于数组则不能这样做, 这也是字符串与字符数组的区别.

指针数组

若干个存储类型相同的指针变量构成的数组.

double *pa[2], a[2][3]

*只管一个

指针数组可以用于对二维数组.

 

多级指针

void指针

 void * [指针名] 

可以通过强制类型转换变为其他类型的指针

显示转换

隐式转换

显式调用是指在代码中调用 隐式调用是编译器后台调用

函数

定义和用法

l函数是一个完成特定功能的代码模块,其程序代码独立,通常要求有返回值,也可以是空值。大括号内称为函数体

l一般形式如下:

<数据类型> <函数名称>( <形式参数说明> ) {

​      语句序列;

​      return[(<表达式>)];

} 

例如

double p(int x);

调用

函数名(实际参数)

实参就是在使用函数时,调用函数传递给被调用函数的数据。需要确切的数据

函数调用可以作为一个运算量出现在表达式中,也可以单独形成一个语句。对于无返回值的函数来讲,只能形成一个函数调用语句。

参数传递 (C语言没有引用)

传递数组参数(非字符串)需要同时传递数组的大小.

复制传递方式

实参的值赋值给形参

  1. 调用函数将实参传递给被调用函数,被调用函数将创建同类型的形参并用实参初始化
  2. 形参是新开辟的存储空间,因此,在函数中改变形参的值,不会影响到实参

指针函数

指针函数的用法

指针函数是一个返回值为指针的函数.

函数中的空间会在结束时释放, 这造成指针无法再访问, 可以将函数内变量申请为static变量延长试用期.

或使用 字符串常量:

char *str = "hello"

但是不能修改*str

总的来说, 指针函数的返回可以是:

  1. 全局变量的地址
  2. static变量的地址
  3. 字符串常量的地址
  4. 动态内存

递归函数和函数指针

递归即递推加回归

例子1: 求阶乘

#include<stdio.h>
int calcu(int x);
int main()
{
	printf("%d\n",calcu(10));
	return 0;
}
int calcu(int x)
{
	if (x==0)
	{
		return 1;
	}
	double factorial = x;
	factorial*=calcu(x-1);
	return factorial;
}

例子2: 斐波拉契数列

int main()
{
	for(int num = 1; num <= 10; num++)
	{
		printf("%d\n",fib(num));
	}
	return 0;
}
int fib(int x)
{
	int fibx;
	if (x == 1 || x == 2)
	{
		return 1;
	}
	fibx = fib(x-1)+fib(x-2);
	return fibx;
}

函数指针

函数指针的核心是一个指针, 存储函数的地址

函数地址代表了函数的入口地址( 函数名)

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

int main()
{
	int m = 10,n = 20;
	int (*p)(int ,int );
	p=add;
	printf("%d\n",p(m,n));
	return 0;
}
int add(int x,int y)
{
	return x+y;
}

函数指针要与函数产生关系, 因此需要返回值类型和参数列表和函数的原型一致, 这里注意解引用 *的优先级, 需要加()

函数指针数组

数组中函数指针指向函数的返回类型和参数列表一致.

例子: 调用qsort进行排序

#include<stdio.h>
#include<stdlib.h>
int stol(const void *x, const void *y);

int main()
{
	int a[10] = {5.7,3,6,4,2,8,9,1};
	qsort(a,sizeof(a)/sizeof(int),sizeof(int),stol);
	for(int k = 0; k<= 9; k++)
	{
		printf("%d\n",a[k] );
	}
	return 0;
}
int stol(const void *x, const void *y)
{
	return *(int *)x - *(int *)y;
}

资源&更深入的阅读资料:

领取资料的神秘通道开启点击链接加入群聊【C语言/C++编程学习基地】:828339809

原网站

版权声明
本文为[编程小鱼六六六]所创,转载请带上原文链接,感谢
https://blog.csdn.net/yx5666/article/details/125054646