数据类型
基本数据类型
基本数据类型的最主要特点是,其值不可以再分解为其它类型,也就是说,基本数据类型是自我说明的
构造数据类型
构造数据类型是根据已定义的一个或多个数据类型用构造的方法来定义的。也就是说一个构造类型的值可以分解成若干个"成员"或"元素"。每个"成员"都是一个基本数据类型或又是一个构造类型
C语言中构造类型:
- 数组类型
- 结构体类型
- 共用体(联合)类型
指针类型
指针是一种特殊的,同时又是具有重要作用的数据类型。其值用来表示某个变量在内存储器中的地址。虽然指针变量的取值类似于整型量,但这是两个类型完全不同的量
空类型
在调用函数值时,通常应向调用者返回一个函数值。这个返回的哈桉树值时具有一定的数据类型的,应在函数定义及函数说明中给以说明,调用后并不需要向调用者返回函数值,这种函数可以定义为"空类型"。其类型说明符为void
变量、常量
符号常量(宏)
#define PRICE 30
整数类型
八进制数 0开头
十六进制 0X或0x开头
short int // short
int
long int // long
long long int // long long
浮点类型
C语言中以十进制表示
标准C允许浮点数使用后缀。后缀为"f"或"F"即表示该数为浮点数。356f和356.是等价的
float
double
long double
以下不是合法的实数
- 345(无小数点)
- E7(阶码标志E前无数字)
- -5(无阶码标志)
- 53.-E3(负号位置不对)
- 2.7E(无阶码)
字符类型
字符常量 使用单引号括起
每个字符常量 用8bit表示
char
signed char //-128 - 127
unsigned char // 0-255
#include<stdio.h>
void main() {
char a,b;
a = 120;
b = 121;
printf("%d, %d\n", a, b); // 120, 121
printf("%c, %c\n", a, b); // x, y
}
小写字母转换为大写字母
小写字母范围 97 - 122
大写字母范围 65 - 90
#include<stdio.h>
void main() {
char a = 'a';
char b = 'b';
a = a - 32;
b = b - 32;
printf("%d, %d", a, b)
printf("%c, %c", a, b)
}
字符串常量
用双引号括起来
所占内存为 字符串所占字节数+1(\0 ASCII 0 为字符串结束标志)
char name[5];
name[0] = 'z';
name[1] = 'z';
name[2] = 'e';
name[3] = 'r';
char name[6] = {'z','z','e','r','\0'};
char name[] = {'z','z','e','r','\0'};
char name[] = "zzer";
布尔类型
_Bool
枚举类型
enum
sizeof
int k = 0;
sizeof(int); // 查看int的大小
sizeof(k);
sizeof k;
signed,unsigned
signed int i; //带符号的整数
unsigned int j; // 无符号整数
数据类型的转换
是临时的转换,并不改变参与运算的变量的类型
自动转换(不同数据类型做混合运算时)
- 不同数据类型转换为同一数据类型后运算
- 转换按数据长度增加的方向进行,以保证精度不减
- 所有浮点运算都是以双精度进行的,即使仅含float单精度运算的表达式
- char型和short型参与运算时,必须先转换成int型
- 赋值运算中,赋值号两边的数据类型不同时,赋值号右边量的类型将转换为左边量的类型。如果右边量的数据类型长于左边,将丢失一部分数据,这样会降低精度,丢失的部分按四舍五入向前舍入
强制类型转换
强制类型转换是通过类型转换运算实现的
类型转换运算:
(类型说明符) (表达式)
(float) a // 把a转换为实型
(int)(x+y) // 把x+y运算结果转换为整型
运算符
算术运算符
+ | 双目运算符 | 左结合 |
- | 减法时为双目运算符,负号时是单目运算符 | 减法时左结合,负号时右结合 |
* | 双目运算符 | 左结合 |
/ | 双目运算符 | 左结合 |
% | 双目运算符(只能操作整数) | 左结合 |
运算符优先级
运算优先级共15级
初级运算符( )、[ ]、->、. 高于 单目运算符 高于 算数运算符(先乘除后加减) 高于 关系运算符 高于 逻辑运算符(不包括!) 高于 条件运算符 高于 赋值运算符 高于 逗号运算符。
位运算符的优先级比较分散。
除了赋值运算符、条件运算符、单目运算符三类的平级运算符之间的结合顺序是从右至左,其他都是从左至右。
优先级 | 运算符 | 名称或含义 | 使用形式 | 结合方向 | 说明 |
1 | [] | 数组下标 | 数组名[常量表达式] | 左到右 | -- |
() | 圆括号 | (表达式)/函数名(形参表) | -- | ||
. | 成员选择(对象) | 对象.成员名 | -- | ||
-> | 成员选择(指针) | 对象指针->成员名 | -- | ||
2 | - | 负号运算符 | -表达式 | 右到左 | 单目运算符 |
~ | 按位取反运算符 | ~表达式 | |||
++ | 自增运算符 | ++变量名/变量名++ | |||
-- | 自减运算符 | --变量名/变量名-- | |||
* | 取值运算符 | *指针变量 | |||
& | 取地址运算符 | &变量名 | |||
! | 逻辑非运算符 | !表达式 | |||
(类型) | 强制类型转换 | (数据类型)表达式 | -- | ||
sizeof | 长度运算符 | sizeof(表达式) | -- | ||
3 | / | 除 | 表达式/表达式 | 左到右 | 双目运算符 |
* | 乘 | 表达式*表达式 | |||
% | 余数(取模) | 整型表达式%整型表达式 | |||
4 | + | 加 | 表达式+表达式 | 左到右 | 双目运算符 |
- | 减 | 表达式-表达式 | |||
5 | << | 左移 | 变量<<表达式 | 左到右 | 双目运算符 |
>> | 右移 | 变量>>表达式 | |||
6 | > | 大于 | 表达式>表达式 | 左到右 | 双目运算符 |
>= | 大于等于 | 表达式>=表达式 | |||
< | 小于 | 表达式<表达式 | |||
<= | 小于等于 | 表达式<=表达式 | |||
7 | == | 等于 | 表达式==表达式 | 左到右 | 双目运算符 |
!= | 不等于 | 表达式!= 表达式 | |||
8 | & | 按位与 | 表达式&表达式 | 左到右 | 双目运算符 |
9 | ^ | 按位异或 | 表达式^表达式 | 左到右 | 双目运算符 |
10 | | | 按位或 | 表达式|表达式 | 左到右 | 双目运算符 |
11 | && | 逻辑与 | 表达式&&表达式 | 左到右 | 双目运算符 |
12 | || | 逻辑或 | 表达式||表达式 | 左到右 | 双目运算符 |
13 | ?: | 条件运算符 | 表达式1? 表达式2: 表达式3 | 右到左 | 三目运算符 |
14 | = | 赋值运算符 | 变量=表达式 | 右到左 | -- |
/= | 除后赋值 | 变量/=表达式 | -- | ||
*= | 乘后赋值 | 变量*=表达式 | -- | ||
%= | 取模后赋值 | 变量%=表达式 | -- | ||
+= | 加后赋值 | 变量+=表达式 | -- | ||
-= | 减后赋值 | 变量-=表达式 | -- | ||
<<= | 左移后赋值 | 变量<<=表达式 | -- | ||
>>= | 右移后赋值 | 变量>>=表达式 | -- | ||
&= | 按位与后赋值 | 变量&=表达式 | -- | ||
^= | 按位异或后赋值 | 变量^=表达式 | -- | ||
|= | 按位或后赋值 | 变量|=表达式 | -- | ||
15 | , | 逗号运算符 | 表达式,表达式,… | 左到右 | -- |
自增自减运算符
++,–
void main() {
int i = 1;
printf("%d", i++); // 返回i自增之前的值1 i = 2
printf("%d", ++i); // 返回i自增之后的值3 i = 3
}
复合赋值运算符
+=
-=
*=
/=
%=
<<=
>>=
&=
^=
!=
逗号运算符
其功能是把两个表达式连接起来组成一个表达式,称为逗号表达式
一般形式
表达式1,表达式2
其求值过程是分别求两个表达式的值,并返回表达式2的值
表达式1,(表达式2,表达式3),表达式4
先运算 逗号表达式 表达式2,表达式3 并返回 表达式3 的值
再运算逗号表达式 表达式1,表达式3,表达式4 返回表达式4的值
C基本语句
- 表达式语句
- 函数调用语句
- 控制语句
- 复合语句
- 空语句
数据输入输出
putchar 字符输出函数
输出单个字符
putchar(‘字符’)
putchar('A');
putchar('a');
getchar 获取输入的一个字符
获取键盘输入的一个字符
char c;
c = getchar();
fgets 从指定文件中获取字符串
函数概要:
fgets 函数用于从指定文件中读取字符串。
fgets 函数最多可以读取 size - 1 个字符,因为结尾处会自动添加一个字符串结束符 ‘\0’。当读取到换行符(’\n’)或文件结束符(EOF)时,表示结束读取(’\n’ 会被作为一个合法的字符读取)。
函数原型:
#include <stdio.h>
char *fgets(char *s, int size, FILE *stream);
参数解析:
参数 含义
s 字符型指针,指向用于存放读取字符串的位置
size 指定读取的字符数(包括最后自动添加的 ‘\0’)
stream 该参数是一个 FILE 对象的指针,指定一个待操作的数据流
返回值:
-
如果函数调用成功,返回 s 参数指向的地址。
-
如果在读取字符的过程中遇到 EOF,则 eof 指示器被设置;如果还没读入任何字符就遇到这种 EOF,则 s 参数指向的位置保持原来的内容,函数返回 NULL。
-
如果在读取的过程中发生错误,则 error 指示器被设置,函数返回 NULL,但 s 参数指向的内容可能被改变。
#include <stdio.h>
#define MAX 1024
int main()
{
char str[MAX];
printf("请输入一个字符串:");
fgets(str, MAX, stdin);
printf("您输入的内容是:%s", str);
return 0;
}
printf 格式输出函数
%[flags][width][.precision][length]specifier
specifier
转换字符
|
参数类型;转换结果
|
---|---|
c
| char;字符 |
d
| int;有符号十进制整数 |
i
| 同上 |
e
| double;以指数形式输出单、双精度浮点数(小写 e) |
E
| 同上(大写 E) |
f
| double;以小数形式输出单、双精度浮点数 |
g
| double;以 %f 或 %e 中较短的输出宽度输出单、双精度浮点数(指数显示小写 e) |
G
| 同上(指数显示大写 E) |
o
| unsigned int;无符号八进制(无前导 0) |
s
| char *;字符串 |
u
| int;无符号十进制 |
x
| unsigned int;无符号十六进制(无前导 0x) |
X
| 同上(无前导 0X) |
p
| void *;指针值 |
n
| int *;存放已写字符的个数 |
%
| 不进行参数转换;% 自身 |
flags
标志
|
含义
|
-
| 指定被转换的参数在其字段内左对齐(默认是右对齐) |
+
| 指定在输出的数前面加上正负号 |
空格
| 如果第一个字符不是正负号,则在其前面加上一个空格 |
0
| 对于数值转换,当输出长度小于字段宽度时,添加前导 0 进行填充 |
#
| 指定另一种输出形式: 1. 如果转换字符为 o,则第一个数字为 0 2. 如果转换字符为 x 或 X,则指定在输出的非 0 值钱加 0x 或 0X 3. 对于转换字符为 e、E、f、g 或 G 的情况,指定输出总是包含一个小数点。另外,对于转换字符为 g 或 G,还指定输出值尾部无意义的 0 将被保留 |
scanf 格式输入函数
即按用户指定的格式从键盘上把数据输入到指定的变量中
scanf是一个标准库函数,函数原型在stdio.h中,与printf相同,C语言也允许scanf函数之前不必包含stdio.h文件
scanf(“格式控制字符串”, 地址列表);
地址是由地址运算符"&"后跟变量名组成的
& 取址运算符
&a 表达式,求变量地址
符号"*"用以表示该输入项读入后不赋予相应的变量,即跳过该输入值
scanf("%d%*d%d", &a, %b);
// 输入 1 2 3 时,1 -> a, 2 x, 3-> b
注意
- scanf没有精度控制,scanf("%5.2f");是非法的
- scanf中要求给出变量地址,如给出变量名会出错
如scanf("%d",a)是非法的,应改为scanf("%d",&a) - 在输入多个数值时,遇到非法数据(如 %d 输入12A 时 A 为非法数据)时即认为数据结束
控制语句
if语句
if (表达式)
printf("条件成立\n");
if (表达式) {
// 语句块
} else if (表达式) {
//语句块
} else {
//语句块
}
switch 语句
switch (表达式) {
case 常量表达式1: 语句或语句块1
case 常量表达式2: 语句或语句块2
case 常量表达式3: 语句或语句块3
default: 语句或语句块
}
switch (c) {
case 'A': printf("90-100\n"); break;
case 'B': printf("80-90\n"); break;
default: printf("no match\n");
}
while循环
入口条件循环
while (表达式) {
// 循环体
}
#include <stdio.h>
#include <stdlib.h>
int main() {
int count = 0;
printf("请输入一行字符:");
while (getchar() != '\n') {
count += 1;
}
printf("你总共输入了%d个字符\n", count);
}
do while循环
出口条件循环
do {
// 循环体
} while (表达式);
for循环
for (初始化变量; 条件表达式; 操作表达式) {
// 循环体
}
C99 允许在 初始化变量中声明变量并赋初值,但声明变量的作用域只能在循环体内
表达式可按实际需要进行省略,但封号不能省略
for (; 条件表达式; 操作表达式)
for (; ; 操作表达式)
for (; ; )
初始化变量 和 操作表达式 可以为逗号表达式
for (int i = 0 , j = 0 ; i < 10; i++, j++) {
printf("current i = %d j = %d\n", i, j);
}
循环嵌套
#include <stdio.h>
#include <stdlib.h>
int main() {
for (int i = 1; i <= 9; i++) {
for (int j = 1; j <= i; j++) {
printf("%-2d * %-2d = %-2d ", i, j, i*j);
}
printf("\n");
}
return 0;
}
break, continue
break; 退出循环体
continue; 跳过本轮循环
continue不会跳过for循环最后的操作表达式 i++,注意与while循环的区别
赋值语句
赋值语句容易和变量说明,赋值表达式混淆,赋值语句必须以;结尾
变量说明
int a,b,c;
变量赋初值
int a = 5;
以下赋初值方式是错误的
int a=b=c=5;
赋值表达式
if ((x=y+5)>0) z=x;
a = 1 // 返回 a 的值
条件运算符
exp1 ? exp2 : exp3
exp1 为真时执行 exp2
为假时执行 exp3
goto 语句
goto 标签名;
标签定义
标签名: 语句
数组
数组声明时最好进行初始化,否则会出现随机的数值
int arr[5]; // 数组下表必须为常量
数组初始化
int arr[3] = {1, 2, 3};
int arr2[3] = {1, 2}; // 未赋值的元素为0
int arr3[] = {1, 2, 3, 4}; // 不指定下标,编译器会自动计算元素个数
int arr4[10] = {[3] = 3,[5] = 5}; // C99特性指定下标赋值
int arr5[10] = {0};
sizeof(arr4) // 计算数组元素占用的总内存大小
// C99 下
//数组下标可以是变量,数组越界修改和访问也不会报错
int n = 10;
int arr[n];
// 字符数组初始化
char str[] = "";
字符串
<string.h> 头文件中定义
strlen
量取字符从头开始到第一个\0为止,返回字符长度不包括\0
返回结果为无符号整型,无符号整型之间做减法是不会返回负值的
char arr[] = "zzer";
printf("%d", strlen(arr));
strcpy,strncpy 字符串拷贝
char str1[] = "this is string1";
char str2[] = "string2";
char str3[20];
strcpy(str1, str2); // 将str2的内容拷贝给str1,包括\0
// str1 的长度应大于str2 否则字符拷贝会出现错误
strncpy(str3, str1, 4); // 将str1拷贝给 str3指定字符长度
strncpy(str1, str2, sizeof(str1) - 1); // strncat 函数会在指定的最大长度之后一字节的位置写入字符串结束符
str1[sizeof(str1) - 1] = '\0';
strcat, strncat 连接字符串
char str1[] = "today is ";
strcat(str1, "saturday"); // 将 "saturday" 连接到 str1后面并自动加上 \0
// strncat(str1, "saturday", 3); // 后面不会自动加上\0
printf("str1 = %s", str1); // str1 = today is saturday
strcmp, strncmp 比较字符串
strcmp(str1, str2); // 若str1 与str2 相等 返回0,否则返回-1
strncmp(str1, str2, n) // 判断str1与str2 前n个字符是否相等,相等返回0,不相等返回-1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char str1[30];
char str2[30];
int i;
char c;
while (1) {
printf("请输入字符1:");
for (i = 0; (c = getchar()) != '\n';i++ ) {
str1[i] = c;
}
str1[i+1] = '\0';
// getchar();
printf("请输入字符2:");
for (i = 0; (c = getchar()) != '\n'; i++) {
str2[i] = c;
}
str2[i+1] = '\0';
printf("str1 = %s\nstr2 = %s\ncmp = %d\n", str1, str2, strcmp(str1, str2));
}
}
二维数组
int i[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
int i[3][4] = {
{1,2,3,4},
{5,6,7,8},
{9,10,11,12}
};
int i[3][4] = {{1},{5},{9}}; // 将每行第一个元素赋值为1,5,9 其余元素为0
int i[3][4] = {0};
int i[3][4] = {[0][0]=1, [1][1]=2,[2][2]=3};
int i[][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
统计输入字符数
一
#include <stdio.h>
#include <string.h>
#define NUM 128
int main()
{
int ch, i, j = 0, max = 0;
int input_num = 0;
int ascii[NUM] = {0};
char count[NUM] = "";
printf("请输入英文文本:");
while ((ch = getchar()) != '\n')
{
ascii[ch]++; // 字符对应的ASCII码加1
input_num++;
}
for (i = 0; i < NUM; i++)
{
if (ascii[i])
{
count[j++] = i;
if (ascii[i] > ascii[max])
{
max = i;
}
}
}
printf("你总共输入了%d个字符,其中不同的字符个数有%d个。\n", input_num, strlen(count));
printf("它们是:%s\n", count);
printf("出现次数最多的字符是\'%c\',它总共出现了%d次。\n", max, ascii[max]);
return 0;
}
二
#include <stdio.h>
#include <string.h>
int main()
{
int counts[20] = {0};
char strs[20] = "";
char c;
_Bool flag;
int index;
printf("请输入字符:");
for (; (c = getchar()) != '\n';) {
flag = 0;
for (int j = 0; j < strlen(strs); j++) {
if (strs[j] == c) {
counts[j]++;
flag = 1;
}
}
if (!flag) {
index = strlen(strs);
strs[index] = c;
counts[index] = 1;
}
}
for (int i = 0; i < strlen(strs); i++) {
printf("字符 %c 出现的次数为:%d\n", strs[i], counts[i]);
}
}
指针
指针变量声明
char *pa; // 定义一个指向字符型的指针变量
int *pb; // 定义一个指向整型的指针变量
取址运算符、取值运算符
取址运算符,获取变量的地址
char *pa = &a;
int *pb = &b;
取值运算符,获取指针变量指向的地址的值
printf("%c, %d\n", *pa, *pb);
避免访问未初始化的指针
指针和数组
#include <stdio.h>
int main() {
char str[30];
printf("请输入字符串:");
scanf("%s", str);
printf("你输入的字符是:%s", str);
return 0;
}
指针是左值;
而数组名只是一个地址常量,它不可以被修改,所以数组名不是左值。
str1 和 &str1 的值相同,但含义是不一样的。str1 表示数组第一个元素的位置,而 &str1 表示的是整个数组的位置(这里将数组看做一个整体)
char str1 = "hello";
char *ps = str1;
printf("%c\n", ps[0]);
printf("%c, %c\n", *ps, *(ps+1)); // 声明指定指针类型,ps+1可直接移向下一个数组元素的地址
char *str = "hello";
printf("%c\n", str[0]); // "h"
指针数组
指针数组是一个数组,每个数组元素存放一个指针变量。
int *p1[5];
char *p2[2] = {
"How are you!",
"I'm fine"
};
for (int i = 0; i < 2; i++) {
printf("%s\n", p2[i]);
}
数组指针
#include <stdio.h>
int main()
{
char *array[5] = {"monday", "tuesday", "wednesday", "thursday", "friday"};
char *(*p)[5] = &array;
printf("%s\n", *array); // monday
printf("%s\n", **p); // monday
}
void指针
void指针称为通用指针,可以指向任意类型的数据
#include <stdio.h>
int main()
{
int i = 100;
char str[] = "monday";
int *pi = &i;
char *ps = str;
void *p;
p = pi;
printf("p = %d, pi = %d\n", p, pi);
p = ps;
printf("p = %s, ps = %s\n", p, ps);
return 0;
}
void指针转换为其它类型指针时,最好进行类型转换
#include <stdio.h>
int main()
{
int i = 100;
void *p = &i;
printf("p = %d, p+1 = %d, (int *)p+1 = %d\n", p, p+1, (int *)p+1); // p = 6422036, p+1 = 6422037, (int *)p+1 = 6422040
}
NULL
当不清楚指针、对象初始化为什么值时,将它初始化为NULL
void *p = NULL;
对NULL指针进行解引用时会抛出error
二级指针(指向指针的指针)
常量
常量无法被修改
- 使用宏定义
#define PRICE 520
- 使用const关键字修饰
const int price 520;
指向常量的指针
const int num = 100;
const int *p = #
- 可以修改指向的地址
- 可以通过解引用读取指针指向的数据
- 不可以通过解引用修改指针指向的数据
常量指针
int num = 100;
int * const p = #
- 指针自身不可以被修改
- 指针指向的值可以被修改
指向常量的常量指针
const int num = 100;
const int * const p = #
// 二级指针
const int * const *pp = &p;
函数
函数定义
类型名 函数名(参数列表)
{
函数体
}
函数的声明
当函数定义在执行代码之后是,函数声明写在前面,让编译器不要报错
类型名 函数名(参数列表);
可变参数
实现可变参数,需要包含一个头文件叫:<stdarg.h>。
这个头文件中有三个宏和一个类型是我们需要用到的,一个类型是 va_list,三个宏,一个是 va_start,一个是 va_arg,还有一个是 va_end。这里的 va就是 variable-argument(可变参数)的缩写。
注:va_start() 的第二个参数是函数的最后一个参数名,而不是参数的数量哦。详见下面代码注释。
#include <stdio.h>
#include <stdarg.h>
int sum(int n, ...);
int sum(int n, ...) // 三个小点是占位符,表示参数个数不确定
{
int i, sum = 0;
va_list vap; // 定义参数列表
va_start(vap, n); // 初始化参数列表,如果是 int sum(int gg, ...); 则这里应该是 va_start(vap, gg);
for (i = 0; i < n; i++)
{
sum += va_arg(vap, int); // 获取参数值
}
va_end(vap); // 首尾工作,关闭参数列表
return sum;
}
int main()
{
int result;
result = sum(3, 1, 2, 3);
printf("result = %d\n", result);
return 0;
}
返回指针的函数
int *p();
使用指针变量作为函数的返回值
char *getChar(char c);
char *getChar(char c) {
// 函数定义
}
不要返回函数内局部变量的指针,局部变量在离开作用域时就被销毁
函数指针
int (*p)();
#include <stdio.h>
int squart(int n);
int squart(int n) {
return n * n;
}
int main() {
int n;
int (*p)(int);
p = squart; // p = &squart
printf("请输入一个整数:");
scanf("%d", &n);
printf("%d * %d = %d\n", n, n, (*p)(n)); // p(n)
return 0;
}
返回函数指针的函数
#include <stdio.h>
int add(int, int);
int sub(int, int);
int calc(int (*fp)(int, int), int, int);
int (*select(char))(int, int);
int add(int num1, int num2) {
return num1 + num2;
}
int sub(int num1, int num2) {
return num1 - num2;
}
int calc(int (*fp)(int, int), int num1, int num2) {
return (*fp)(num1, num2);
}
int (*select(char op))(int, int) {
switch (op) {
case '+': return add;
case '-': return sub;
}
}
int main() {
int num1, num2;
char op;
int (*p)(int, int);
printf("请输入一个表达式<1+2>:");
scanf("%d%c%d", &num1, &op, &num2);
p = select(op);
printf("%d %c %d = %d\n", num1, op, num2, (*p)(num1, num2));
return 0;
}