编译器
:将高级语言翻译成数字指令码(机器语言)
编译
:gcc 源代码.c -o 可执行代码.exe
.c
:表示源代码文件
.exe
:可执行的代码
getchar()
:读取一个字符
include
:具有共享作用
stdio.h
:C编译器软件包的标准部分,标准的输入输出头文件
#include
:这行代码具有预处理(preprocessing)作用,称为头文件
注释风格
:// 是C99新增的一种风格注释,普遍用于C++和Java
#include /*头文件*/
/**
* @brief main函数,C语言首先调用的函数
*
* @return float main函数返回一个float
*/
float main (void) { //不符合标准
printf("I am"); /*读取一个Enter键*/
return 0;
}
void
:参数是空
int
:返回值为整数
int main(void) //固定标准形式
int main() //C90标准可接受,C99、C11标准不接受,标准越来越严格
二者的参数和返回值保持一致
#include
int buffer(void); /*声明buffer函数*/
int main(void)
{
printf(buffer() + "Yes, Let us jump up right in .");/*使用buffer函数*/
return 0;
}
int buffer(void) /*定义buffer函数定义,必须与声明的函数一致*/
{
printf("Are you ready ?\n");
return 0;
}
注意
#define DAYS_PER_YEAR 365 (✔)
#define DAYS_PER_YEAR = 365 (❌)
%.2f
:输出小数位后两位
%lf
:打印double型数据
%ld
:打印long型数据
%hd
:打印short型数据
%u
:打印unsigned型
unsigned
:非负
_Bool
:布尔类型(整数)
_Complex
:复数
_Imaginary
:虚数
符号位 | 小数部分 | 指数部分 |
---|---|---|
+ | .3141592 | 1 |
结果:3.141592
八进制(octal)
:用0开头
十六进制(hexadecimal)
:以 0X 或 0x 开头
将数以xx进制进行显示
十进制(decimal )
:%d八进制(octal)
:%o (哦~)十六进制(hexadecimal)
:%x带前缀进制数显示
八进制(octal)
:%#o十六进制(hexadecimal)
:%#x %#X代码
#include
int main (void)
{
int x = 100; /*可换成0100、0x100进行演示*/
printf("进制数显示\n");
printf("\tdec = %d; octal = %o; hex = %x\n", x, x, x);
printf("带前缀进制数显示\n");
printf("\tdec = %d; octal = %#o; hex = %#x\n", x, x, x);
return 0;
}
结果
进制数显示
dec = 100; octal = 144; hex = 64
带前缀进制数显示
dec = 100; octal = 0144; hex = 0x64
C99标准添加了_Bool类型,C语言用1表示true,用0表示false;
所以_Bool实际上是一种占1位
的整数类型(1字节8位)
float
:至少能表示6位有效数字,通常占32位,其中8位用于表示符号和指数,24位表示非指数部分,也叫做尾数或有效数
double
:至少能表示10位有效数字,通常64位多出来的32位表示非指数部分,增加了有效数字
的位数(即提高了精度),还减少了舍入误差
代码
#include /*新增了两个头文件*/
#include /*浮点数的上下溢==>FLT_MAX、FLT_MIN*/
#include /*整数上溢==>INT_MAX*/
/*整型上溢+浮点数上溢和下溢*/
int main (void)
{
/*1. 整数上溢*/
int big_int = 2147483647;
printf("The big int data is %d\n", ++big_int);
/*2. 浮点数上溢*/
float big_float = 3.4e38;
printf("The big float data is %f\n", big_float * 10);
/*3. 浮点数下溢*/
float small_float = 10.0 / 3;
printf("The small float data is %f\n", small_float);
printf("The MAX int data is %ld\n", INT_MAX);
printf("The MAX int data is %ld\n", INT_MIN);
printf("The MAX float data is %f\n", FLT_MAX);
printf("The MIN float data is %f\n", FLT_MIN);
return 0;
}
结果
The big int data is -2147483648
The big float data is 1.#INF00
The small float data is 3.333333
The MAX int data is 2147483647
The MAX int data is -2147483648
The MAX float data is 340282346638528860000000000000000000000.000000
The MIN float data is 0.000000
strlen()函数
\0
结尾,字符串大小不包括\0
char型数组
大小计算同上sizeof()函数
字符串
后的**\0**结尾符char型数组
字节大小int values[5]
20
5
不适用于二维数组遍历
代码
#include
#include /*strlen函数的头文件*/
#define NAME "廖述幸"
int main (void)
{
char name[40] = NAME; /*C语言中没有String类型,字符串存储通常以\0结尾,不计算\0*/
printf("What is your name?\n My name is %s\n", name);
printf("How about name's length?\n It is %d\n", strlen(name)); /*遇到字符串末/0结束===>字符串大小*/
printf("How about name's size?\n It is %d\n", sizeof(name)); /*得到===>char数组大小*/
return 0;
}
结果
What is your name?
My name is 廖述幸
How about name’s length?
It is 6
How about name’s size?
It is 40
'x’是一个字符 | "x"是一个字符串 | |
---|---|---|
存储形式 | x | X\0 |
来源:C90标准新增const关键字
作用:限定一个变量
只读
优势:比#define更为灵活
const int MONTHS = 12; /*正确的定义,MONTHS在程序中不能修改*/
#define和const的区别(参考链接)
转换说明:%s %d %f
%4d
:如果打印的数字没有4位,会用空格补全
%5.2f
:字段宽度5位,小数点后2位
%10.2s
:字段宽度10位,有效位数2位,右对齐
%-10.2s
:字段宽度10位,有效位数2位,-表示左对齐
%-03.2d
:字段宽度3位,有效位数2位,用0代替空格填充字段宽度,超出字段宽度,则不需要填充,直接进行显示
p72
代码
#include
int main (void)
{
char num[40] = "廖述幸";
printf("|%10.2s|\n", num); /*可以换成 %10.4s 进行演示,通常中文占2字节*/
printf("|%-10.2s|", num);
return 0;
}
结果
| 廖|
|廖 |
代码
#include
#define NAME "廖述幸"
#define ADDRESS "湖南省武冈市xxx镇xx村xx组"
#define PHONE_NUMBER "166xxxx7152"
int main (void)
{
printf("%-20s %-40s %-20s\n", "姓名", "住址", "手机号码");
printf("%-20s %-40s %-20s\n", NAME, ADDRESS, PHONE_NUMBER);
return 0;
}
结果
姓名 住址 手机号码
廖述幸 湖南省武冈市xxx镇xx村xx组 166xxxx7152
// 1、正常输出
printf("%d\t%d\t%d\n", 312, 2344, 136);
printf("%d\t%d\t%d\n", 400, 234, 3412);
// 2、使用固定字段宽度进行输出,字符宽度为6,不足空格补齐,默认右对齐
printf("%6d\t%6d\t%6d\n", 312, 2344, 136);
printf("%6d\t%6d\t%6d\n", 400, 234, 3412);
---输出结果比较---
312 2344 136
400 234 3412
312 2344 136
400 234 3412
float salary = 123.00;
int width, precision;
printf("Enter a width and a precision:\n");
scanf("%d %d", &width, &precision);
printf("%*.*f", width, precision, salary); // *表示字段宽度,由键盘输入进行精度控制
%s和scanf函数的输入
char name[30];
printf("Input one name:");
scanf("%s", name); // 只会读取部分,遇到空白处停止写入
printf("%s", name);
return 0;
---输出结果---
Input one name: carter I love you
carter
char型数组
:使用scanf输入时,前面不需要==&==
Scanf并不是最常用的输入!!!
%s
:不接收含空白的字符串,空格处终止%c
:可接收空格字符,仅读一个字符原因:遇到第一个空白,scanf认为工作完成,后续数据不再写入当前变量,只保存在输入缓冲区中!!!
注意
scanf("%c", &ch); /*从输入中第一个字符开始读取*/
scanf(" %c", &ch); /*从输入中第一个非空白字符开始读取*/
代码
#include
int main (void)
{
int age;
float assets;
char pet[30]; /*这是一个字符串*/
printf("Please input your value:");
scanf("%d", &age);
scanf("%f", &assets);
scanf("%s", pet); /*字符数组不需要&*/
printf("age = %d\n", age);
printf("assets = %f\n", assets);
printf("pet = %s\n", pet);
char c;
printf("输入一个字符:\n");
getchar(); /*必须来一个这个不然会跳过下一个输入*/
scanf("%c", &c); /*需要&符号,可输入空白字符*/
printf("[%c]", c); /*可以接收空白*/
}
结果
Please input your value:12 11.90 hello world~
age = 12
assets = 11.900000
pet = hello输入一个字符:
[ ]
注意:hello world===>仅读取hello
scanf
输入,自定义字段宽度代码
#include
int main (void)
{
int width, number; /*width提供字段宽度,number是待打印的数字*/
printf("Please input the String's width:_____\b\b\b");
scanf("%d", &width);
printf("\nThe number is ");
getchar(); /*防止闪过,往往在scanf上添加*/
scanf("%d", &number);
printf("格式化输出:[%-*d]", width, number);
getchar();/*防止.exe可执行文件闪退问题,必须得来两个,无情*/
getchar();/*防止.exe可执行文件闪退问题 缓存区有字符在,用getchar()清空缓存区,最好使用循序*/
return 0;
}
结果
Please input the String’s width:_ 9__
The number is 10
格式化输出:[10 ]
代码
#include
/*输入catch 22 跳过字符串catch读取value=22*/
int main (void)
{
int value;
printf("Input String is ");
scanf("%*s %d", &value); /*忽略字符串,取后面的Int型数据*/
printf("value = %d", value);
return 0;
}
演示
Input String is value 11
value = 11
printf
函数:注意不换行strlen
函数代码
#include
#include
/*
获取字符串大小
①使用printf函数
②使用strlen函数
*/
int main (void)
{
char name[20];
printf("Input Your name:");
scanf("%s", name);
int lenthByPrintf, lenthByStrlen;
lenthByPrintf = printf("%s", name); /*Printf返回值记录了字符数量===>注意不要换行*/
lenthByStrlen = strlen(name); /*Strlen函数获取字符数量,需添加头文件string.h*/
printf("\nlenthByPrintf is %d, lenthByStrlen is %d", lenthByPrintf, lenthByStrlen);
return 0;
}
演示
Input Your name:carter
carter
lenthByPrintf is 6, lenthByStrlen is 6
注意
lenthByPrintf = printf("%s", name); ===> len = 6
lenthByPrintf = printf("%s\n", name); ===> len = 7 (换行符也被计算)
整数➗整数 = 整数
浮点数➗浮点数 = 浮点数
代码
#include
/*除法运算*/
int main (void)
{
int a = 1/3;
float b = 1/3;
float c = 1.0/3.0;
printf("int a = %d\n", a);
printf("float b = %.2f\n", b);
printf("float c = %.2f\n", c);
return 0;
}
演示
int a = 0
float b = 0.00
float c = 0.33
问题
常见的错误
#include
#define b 2
int main (void)
{
const int a = 1;
printf("%d", a++); /*其中a是常量,不可变*/
printf("%d", b++);
return 0;
}
结果
编译错误
- 表达式a、b必须是可修改的值
++a与b++
int number = 1;
printf("number=%d", number--); // 后缀模式->输出 1
printf("number=%d", --number); // 前缀模式->输出 -1
// ++x; <==等价==> x = x + 1;
Question
++num*num
num*num++
之间的区别???
sizeof
:以字节为单位返回运算对象的大小
副作用(side effect)
printf() \\ 副作用就是显示的信息
int i;
i = 1; \\ 副作用就是将变量i的值修改为1
序列点(sequence)
int mice = (int) 1.3 / (int) 1.7;
C99标准规定:
实参
:argument(有定值的,如5、int a = 1的a)形参
:parameter(int a)阿拉伯数字中仅有0表示false
#include
int main(void)
{
int n = 3;
while(n)
printf("%2d is true!\n", n--);
printf("%2d is false!\n", n);
return 0;
}
结果如下:
3 is true!
2 is true!
1 is true!
0 is false! ---仅有0表示false
_Bool类型
#include
int main(void)
{
long num;
long sum = 0L;
_Bool input_is_good;
printf("please input a number:\n");
input_is_good = (scanf("%ld", &num) == 1);
while (input_is_good) {
sum = sum + num;
printf("Please enter next integer (q to quit) :\n");
input_is_good = (scanf("%ld", &num) == 1);
}
printf("Those integers sum to %ld.\n", sum);
return 0;
}
======运行结果======
please input a number:
12
Please enter next integer (q to quit) :
12
Please enter next integer (q to quit) :
1.1
Please enter next integer (q to quit) :
Those integers sum to 25.
=====结论:输入int值_Bool都将是1,继续迭代!!!其他类型退出。=====
Scanf()函数
匹配
,返回值为1不匹配
,返回值为0算数运算符 > 关系运算符 > 赋值运算符
for (; test; ;)
while (test) /*二者效果相同*/
%%
int main (void)
{
printf("%%");
return 0;
}
代码
#include
#define FORMAT "%s! C is pretty good!\n"
#define PERCENT '%'
int main (void)
{
printf(FORMAT, FORMAT);
printf("%c", PERCENT);
return 0;
}
结果
%s! C is pretty good!
! C is pretty good!
%
getchar
:无参,从输入队列中返回下一个字符
putchar
:有参,和printf效果相同,但只能接收单个字符
代码
#include
int main (void)
{
char ch;
printf("Input your name: ");
scanf("%c", &ch);
ch = getchar(); /*获取输入的下一个字符*/
printf("ch = [%c]\n", ch);
return 0;
}
演示
Input your name: carter
ch = [a]
代码
#include
int main (void)
{
char ch;
ch = getchar();
printf("ch = [%c]", ch);
return 0;
}
演示
q
ch = [q]
联合使用
#include
#define SPACE ' '
/*getchar与putchar的妙用*/
int main (void)
{
char ch;
ch = getchar(); /*获取一个字符*/
while (ch != '\n') /*以换行符为一行结束的标志*/
{
if (ch == SPACE)
putchar(ch); /*当前字符是空格则不改变*/
else
putchar(ch + 1); /*非空格,打印ASCII码加1的字符*/
ch = getchar(); /*继续获取下一个字符*/
}
putchar(ch); /*打印换行符*/
return 0;
}
演示
CALL MY NAME.
DBMM NZ OBNF/
输入一串字符,仅仅转换所有字母
,其它保留原样!
问题:如果通过if
条件,列出所有可能性太繁琐
解决方案:ctype.h
头文件包含一系列专门处理字符的函数
Page156
代码
#include
#include /*C语言中专门处理字符的头文件*/
#define SPACE ' '
int main (void)
{
char ch = getchar(); /*读取一个字符*/
while ( ch != '\n')
{
if (!isalpha(ch)) /*不是字母就保留原样的条件*/
putchar(ch);
else
putchar(ch + 1);
ch = getchar ();
}
putchar(ch);
return 0;
}
结果
LOOK! It’s a programmer!
MPPL! Ju’t b qsphsbnnfs!
#include
int main (void)
{
int a;
printf("Input a number: ");
scanf("%d", &a);
if (a < 0)
printf("a<0\n");
else if (a < 10)
printf("a是个位数\n");
else if (a < 100)
printf("a是两位数");
else
printf("a > 100");
return 0;
}
#include
int main (void)
{
int a;
printf("Input a number: ");
scanf("%d", &a);
if (a < 0)
printf("a<0\n");
else
if (a < 10)
printf("a是个位数\n");
else
if (a < 100)
printf("a是两位数");
else
printf("a > 100");
return 0;
}
C99标准增加了可替换逻辑运算符
(&&、||、!)的英文拼写
传统写法 | iso646.h |
---|---|
&& | and |
|| | or |
! | not |
C语言中唯一的三元运算符
x = (y < 0) ? y : -y
如果 y < 0 ===> x = y;
如果 y >= 0 ===> x = -y;
continue
:跳过本次迭代的剩余部分,直接进入下次循环
break
:终止包含它的循环
goto
:基本不使用
《C primer plus (第6版)》 P172
long n;
scanf("%ld", &n);
while (n >= 0)
{
/*表达式*/
scanf("%ld", &n);
}
long n;
while ((scanf("%ld", &n)) == 1 && n >= 0)
{
scanf("%ld", &n);
}
代码
#include
void integerChange(int * x, int * y);
int main (void)
{
int x = 10;
int y = 11;
printf("交换前: x = %d y = %d\n", x, y);
integerChange(&x, &y);
printf("交换后: x = %d y = %d\n", x, y);
return 0;
}
/*数据交换*/
void integerChange(int *x, int *y)
{
int temp;
temp = *x;
*x = *y;
*y = temp;
}
结果
交换前: x = 10 y = 11
交换后: x = 11 y = 10
三种方法
/*函数声明的三种方法*/
#include
/*ANSI之前的形式声明函数原型*/
void dibs(); /*第1种:旧式函数声明*/
/*ANSI C形式声明函数原型*/
void sum01 (int a, int b); /*第2种:标准形式*/
void sum02 (int, int); /*第3种:省略变量名*/
int main (void)
{
dibs(2, 3);
sum01(3, 4);
sum02(4, 5);
return 0;
}
/*第1种方法*/
void dibs(x, y)
int x, y;
{
printf("x + y = %d\n", x+y);
return;
}
/*第2种方法*/
void sum01 (int a, int b)
{
printf("a + b = %d\n", a+b);
return;
}
/*第3种方法*/
void sum02 (int a, int b)
{
printf("a + b = %d\n", a+b);
return;
}
结果
x + y = 5
a + b = 7
a + b = 9
终极方法
/*函数声明定义一体化*/
#include
void sum (int a, int b)
{
printf("a + b = %d", a + b);
return;
}
int main (void)
{
sum(2, 3);
return 0;
}
结果
a + b = 5
ar[i] == *(ar+1) == *++ar
根据首地址取值
#include
void sum (int * firstAddr, int arraysLen);
int main (void)
{
int arrays[5] = {1, 2, 3, 4, 5};
int arraySize = sizeof(arrays) / sizeof(arrays[0]); /*数组大小获取*/
printf("数组大小:%d\n", arraySize);
printf("arrays = %#p\n", arrays);
printf("&arrays[0] = %#p\n", &arrays[0]); /*二者等价*/
sum(&arrays[0], arraySize);
return 0;
}
/**
* @brief 计算数组内数字总和
* firstAddr:首地址
* arraysSize: 数组大小
*/
void sum (int * firstAddr, int arraysSize)
{
int total = 0;
for (int i = 0; i < arraysSize; i++, firstAddr++) /*当前地址:firstAdrr 下一个数据地址:firstAdrr + 1*/
{
printf("第%d个数\t地址:%#p 值=%d\n", i + 1, firstAddr, *firstAddr);
total += *firstAddr;
}
printf("总和=%d", total);
return;
}
结果
数组大小:5
arrays = 0X000000000061FE00
&arrays[0] = 0X000000000061FE00
第1个数 地址:0X000000000061FE00 值=1
第2个数 地址:0X000000000061FE04 值=2
第3个数 地址:0X000000000061FE08 值=3
第4个数 地址:0X000000000061FE0C 值=4
第5个数 地址:0X000000000061FE10 值=5
总和=15
注意
int values[3] = {1,2,3};
const int *ptr = values;
ptr++; /*没有毛病,指向values[1]*/
printf("%d", *ptr); ===>输出2
const int a = 1;
a++; /*有毛病,a是不可更改的常量*/
*为准,左值右指针
const int * ptr
int * const ptr
const int * const ptr
int zipoo[4][2]
zipoo数组名
:首元素的地址**&zipoo[0]**
zipoo[0]
:&zipoo[0] [0]地址
*(zipoo[0])
:zipoo[0] [0]值
*zipoo
:&zipoo[0] [0]地址
**zipoo
:zipoo[0] [0]值
int (*ptr) [2]
:ptr指向一个内含两个int类型值的数组
int * ptr [2]
:ptr内含两个指针元素的数组
等价关系
zippo[m][n] == *(*(zippo + m) + n)
pz[m][n] == *(*(pz + m) + n)
int pt[][4]; //一个内含4个类型值的数组指针
int [][4]; //可以省略数组名
int (*pr)[4]; //两者等价
形参声明
int sum (int ar[][], int rows); //错误声明
int sum (int ar[][4],int rows); //正确声明
int sum (int ar[3][4], int rows); //正确声明,3会被编译器自动忽略,同上
int sum (int (*ar)[4], int rows); //同上
C99新增了变长数组
,属于C语言的新特性
变长数组
:动态分配普通数组
:静态分配函数声明
int sum (int rows, int cols, int ar[rows][cols]); /*标准形式,注意先后顺序不可修改*/
int sum (int, int, int ar[*][*]); /*C99/C11标准规定,可以省略原型中的形参名,但数组必须用✳代替省略的维度*/
典型用法
int diva[2] = {10, 20};
(int [2]) {10, 20}; /*匿名函数*/
int sum ((int [2]) {10, 20, 30}, 3);
/*
此时的匿名函数:
①是内含3个int类型值的数组,和数组名类似;
②也是该数组首元素的地址;
*/
字符数组:一个装着字符串字面量的副本
字符串:以\0
结尾
指针表示法:拥有递增操作++
指针数组
普通数组
如果要改变字符串或为字符串输入预留空间,不要使用指向字符串字面量的指针
p281
scanf()和转换说明%s只能读取一个单词。gets()函数是读取整行输入,遇到换行符
结束。
读取的字符后添加一个空字符,使之成为一个字符串。
不存储换行符
代码
#include
#define MAX 4
int main (void)
{
char str [MAX];
printf("请输入一个字符串:");
gets(str);
puts(str);
return 0;
}
问题
缓冲区溢出
(buffer overflow)
fgets()函数通过第2个参数限制读入的字符数量来解决缓冲区溢出的问题。
n-1
个字符存储换行符
不换行
读取错误
:返回NULL问题
读取到n-1个字符截至,后面的字符仍然存放在缓冲区
中,如果不清理缓冲区,下一次会读取遗留在缓冲区的字符!
代码
#include
#define MAX 10
int main (void)
{
char p[MAX];
printf("请输入字符串:");
fgets(p, MAX, stdin); /*实际读取字符MAX-1个*/
fputs(p, stdout); /*不自动添加换行符*/
puts(p); /*自动添加换行符*/
return 0;
}
12
123
1234
12345
问题
fgets()函数
会读取换行符,所以如何删除存储在字符串中的换行符呢?
代码
/*删除fgets()函数读取的换行符*/
#include
#define MAX 10
int main (void)
{
char words[MAX];
int i;
printf("Enter strings(Empty line to quit): ");
while (fgets(words, MAX, stdin) != NULL && words[0] != '\n')
{
i = 0;
while (words[i] != '\n' && words[i] != '\0')
i++;
if (words[i] == '\n') /*遇到\n符号就把它替换为空字符*/
{
printf("第%d次,偶遇换行符...\n", i);
words[i] = '\0';
}
else /*如果遇到空字符,就丢弃输入行的剩余字符*/
{
printf("第%d次,与空字符相遇...\n", i);
while (getchar() != '\n') /*读取的字符是\0,字符串结束的标识符,读取完毕,清除缓冲区字符数据*/
continue;
}
puts(words); /*最后输出读取的字符串*/
}
return 0;
}
特殊的fgets()函数
,只能从标准输入(stdin)中读取数据,不需要第3个参数
丢弃换行符
空指针
处理函数
换行符
空字符
停止输出\0
结尾)char * int
gets()函数丢弃换行符
代码
char mesg [] = "Everything is ok!";
puts(mesg + 3); /*注意此时的起始位置是从 r开始打印*/
puts(&mesg[3]); /*效果同上,注意传递的参数类型是指针*/
结果
rything is ok!
rything is ok!
===是不是有点不可思议?===
换行符
fgets()函数保留换行符
代码
/*自定义打印函数1*/
void putx1(const char * words) /*需要打印的字符串不能变动*/
{
while (*words != '\0') /* while (*words)可以作为条件 */
putchar(*words++); /*指针类型典型应用*/
}
/*自定义打印函数2*/
void putx2(const char strings[])
{
int i = 0;
while (strings[i] != '\0')
putchar(strings[i++]); /*数组类型*/
}
作用:拼接字符串,把第2个字符串的备份附加到第1个字符串末尾!
问题:会覆盖第1个字符串末尾的空字符\0
吗?
缺点:无法确定第1个数组是否有足够多的容量,与gets()函数相似,会导致严重后果缓冲区溢出
!!!
前10个字符
拼接到第一个字符之后代码
#include
#include
int main (void)
{
char first[100] = "carter ";
char * second = "悯Aristo 海角天涯-廖";
strncat(first, second, 8);
puts(first);
puts(second);
return 0;
}
结果
carter 悯Aristo
悯Aristo 海角天涯-廖
strcmp(A, B)
非零都为"真"
缺点:从头到尾比较两个字符串
问题:仅比较两个单词前缀
是否相同时,该如何进行处理?
代码
#include
#include
#define ANSWER "hello"
const int SIZE = 6;
int main (void)
{
char try[SIZE];
printf("Enter a word: ");
scanf("%s", try); /*字符数组名就代表首元素地址*/
printf("value01 = %d\n", try == ANSWER); /*此时比较的是两个地址是否相同*/
/*strcmp()函数,此时比较的是两个字符串的内容,同:0,不同:1*/
printf("value02 = %d\n", strcmp(try, ANSWER));
/*=====!错误的理解!=====*/
/*比较两个地址指向的内容是否一致,同:1(true) 不同:0(false)*/
printf("\n%#p\n%#p\nvalue03 = %d",*try, *ANSWER, *try == *ANSWER);
return 0;
}
演示
value01 = 0 不同
value02 = 0 strcmp相同!错误的理解!
0X0000000000000068
0X0000000000000068
value03 = 1 相同,指向同一地址元素
优点:可以比较两字符串不同的地方,也可以比较指定位置的字符
知识拓展:#define和const的区别(参考链接)
代码
#include
#include
#define MAX 6 /*正确声明:表示一个常量 预编译:编译前的处理 常数*/
//const int MAX = 6; /*错误:Int型变量,初始化为常数!variable-sized object may not be initialized可以用变长数组,但是变量不能初始化!!!*/
int main (void)
{
const char *words[MAX] =
{
"aristoxxx01", "aristoxxx02",
"filename", "teleScope",
"aristoCritic", "glove"
};
int count = 0;
for (int i = 0; i < MAX; i++)
if (strncmp("aristo", words[i], 6) == 0)
{
printf("Found: %s\n", words[i]);
count++;
}
printf("The list contained %d words beginning"
" with aristo.\n", count);
return 0;
}
演示
Found: aristoxxx01
Found: aristoxxx02
Found: aristoCritic
The list contained 3 words beginning with aristo.
性能
缺点
代码
char * ps;
const char * orig = "beast";
char copy[SIZE] = "Be the best that you can be.";
ps = strcpy(copy + 7, orig); /*ps返回值:指向copy中第8个元素之后*/
puts(copy);
puts(orig);
puts(ps);
结果
Be the beast ===将后面的全部用beast覆盖掉===
beast ===需要拷贝的源字符===
beast ===copy第8个元素后的字符===
语法错误
char target[10];
target = "carter"; /*语法错误,编译不通过*/
puts(target);
strncpy(A, B, n)
空字符
,所以必须A[MAX - 1] = ‘\0’,保证A成立为字符串声明在stdio.h中,能够把数据写入字符串,将多个元素组合成一个字符串,相当于字符串的格式化
字符串组合函数
char formal[100]; /*目标字符串声明*/
char firstName[20] = "廖";
char lastName[20] = "述幸";
double price = 379.99;
sprintf(formal, "%s, %-4s, $%6.2f", lastName, firstName, price); /*字符串组合函数*/
puts(formal); /*输出组合后的字符串*/
结果
述幸, 廖 , $379.99
字符串
atoi()函数
:将字符串转变成int类型代码
/*命令行参数*/
#include
int main (int argc, char * argv[])
{
printf("命令行中字符串数量:%d\n", argc);
printf("命令行中参数值:\n");
for (int i = 0; i < argc; i++)
printf("\targv[%d] = %s\n", i, argv[i]);
return 0;
}
=====注意=====
一般需要对字符串个数进行判定。
如:if(argc < 2) return -1;
结果
PS D:\FileDir\Java\VSCode\CLanguage\Chapter-11> .\command_line_argument 11 12 "hello" world "this is my gloves"
命令行中字符串数量:6
命令行中参数值:
argv[0] = D:\FileDir\Java\VSCode\CLanguage\Chapter-11\command_line_argument.exe
argv[1] = 11
argv[2] = 12
argv[3] = hello
argv[4] = world
argv[5] = this is my gloves
代码
#include
#include
#include
void ToUpper (char * str);
int main (void)
{
char strings[] = "carter no\nhello\nworld\n";
char * find;
find = strchr(strings, '\n'); //找到第一处换行符
if (find) // 如果地址不是NULL
*find = '\0'; //字符串结束符号
ToUpper(strings);
puts(strings);
return 0;
}
/**
* @brief 将字符串里所有字符大写
*
* @param str 字符串指针
*/
void ToUpper (char * str)
{
while (*str)
{
*str = toupper(*str); // 仅能作用于单个字符
str++;
}
}
演示
CARTER NO
5种存储类别
存储类别 | 存储期 | 作用域 | 链接 | 声明方式 |
---|---|---|---|---|
自动 | 自动 | 块 | 无 | 块内 |
寄存器 | 自动 | 块 | 无 | 块内,使用关键字register |
静态外部链接 | 静态 | 文件 | 外部 | 所有函数外 |
静态内部链接 | 静态 | 文件 | 内部 | 所有函数外,使用关键字static |
静态无链接 | 静态 | 块 | 无 | 块内,使用关键字static |
花括号
括起来的代码区域goto
语句的标签函数外
文件作用域变量:也称全局变量
static
文件作用域变量(静态全局变量)翻译单元:一个源代码文件和它所包含的头文件
文件作用域
变量都具有静态存储周期和 static
关键字修饰的代码
#include
int main (void)
{
for(int i = 1; i <= 10; i++)
{
static int total = 0; /*静态、无链接、块作用域的变量,静态变量未被初始化就会自动初始化为0,仅初始化一次*/
total += i;
printf("第%d次, total = %d\n", i, total);
}
return 0;
}
结果
第1次, total = 1
第2次, total = 3
第3次, total = 6
第4次, total = 10
第5次, total = 15
第6次, total = 21
第7次, total = 28
第8次, total = 36
第9次, total = 45
第10次, total = 55
关键字:extern
tools.h
/*静态文件作用域*/
static int externalNumber = 9;
controller.c
#include
#include "tools.h" /*externalNumber所在头文件,双引号表明被包含的文件位于当前目录中*/
/*外部链接的静态变量,必须如此声明*/
extern int externalNumber;
int main (void){
printf("externalNumber = %d", ++externalNumber);
return 0;
}
结果
externalNumber = 10
ANSI C库提供了rand()函数
生成随机数—伪随机数生成器,同样的代码运行两次,得到同样的随机数
解决方案:使用time()函数,重置种子
malloc()函数
指针接收
stdlib.h
头文件中NULL指针
,调用exit()函数
退出程序
free()函数
内存数量
在程序执行期间自动增加或减少free()
进行释放free()
会导致内存泄漏(memory leak),内存耗尽!!!3种创建数组的方法
指针
—调用malloc()函数,将其返回值赋给指针后进先出
)动态内存
比栈内存
慢!总结:左值右指针不变
文本内容
二进制内容
文本文件格式
二进制文件格式
C提供两种访问文件的途径
fopen(文件名, 打开模式)
模式字符串 | 含义 |
---|---|
“r” | 读模式 |
“w” | 写模式;清除文本内容,不存在就创建 |
“a” | 写模式;尾部新添,不存在就创建 |
“r+” | 更新模式;读写文件 |
“w+” | 更新模式;读写文件,清除文本内容,不存在就创建 |
“a+” | 更新模式;读写文件,文件末尾添加,不存在就创建 |
代码
fprintf(stdout, "Do one's best level"); /*标准输出*/
printf("Do one's best level"); /*作用同上*/
int i;
fscanf(stdin, "%d", &i); /*stdin 标准输入*/
scanf("%d", &i); /*同上*/
rewind(fp); /*回到文件开始处*/
fscanf(fp, "%s", words) == 1; /*扫描文本内容,扫描单词(不含空格)*/
fgets
(字符数组, 字符串大小, 文件指针):保留换行符fputs
(字符数组,文件指针):不添加换行符fseek()函数
例:fseek(file, 0L, SEEK_SET) —> 定位至文件开始处
模式 | 偏移量的起始点 |
---|---|
SEEK_SET | 文件开始处 |
SEEK_CUR | 当前位置 |
SEEK_END | 文件末尾 |
ftell()函数
函数调用 | 效果 |
---|---|
fseek(file, 0L, SEEK_SET) | 定位至文件开始处 |
fseek(file, 0L, SEEK_CUR) | 保持当前位置不动 |
fseek(file, 0L, SEEK_END) | 定位至文件结尾 |
fseek(file, ftell-pos, SEEK_SET) | 到距离文件开始处 ftell-pos 的位置,ftell-pos 是 ftell() 的返回值 |
潜藏的问题
long类型
的返回值类型,导致数据大小受限,并不适用于大数据时代。
新类型 fpos_t (代表 file position type , 文件定位类型)
fpos_t不是基本数据类型
函数原型
作用:把指定的字符放回输入流中,ANSI C标准保证每次只放回一个字符。
参数:int ungetc(int c ,FILE *fp)
作用:刷新缓冲区,用于更新流(任何读写模式)
参数:int fflush(FILE *fp)
问:如何在文件中保存数值数据?
答:使用fprintf()
将数值转化成字符串
double num = 1. / 3.;
fprintf(fp, "%f", num);
弊:改变转换说明,就会改变存储该值所需要的空间数量,也会导致存储不同的值!并且无法恢复为更高的精度!
问:如何保证数值在存储前后一致?
答:最精确的做法是使用与计算机相同的位组合
来存储。
解:因此,double 类型的值应该存储在一个 double大小的单元中。
如果以程序所用的表示法把数据存储在文件中,则称为以二进制形式
存储数据。不存在从数值形式—>字符串的转化过程
对于标准的 I/O,fread()
和 fwrite
函数用于以二进制形式处理数据。
fwrite()函数原型
作用:把二进制数写入文件
参数:size_t fwrite (const void * restrict ptr, size_t size, size_t nmemb, FILE * restrict fp);
案例:fwrite(buffer, 256, 2, fp)
把2块256字节的数据从 buff 写入文件fp,返回写入的的字符大小。
fread()函数原型
作用:读取文件内容
参数:size_t fread(void * restrict ptr, size_t size, size_t nmeb, FILE * fp);
案例:fread(earning, sizeof(double), 10 fp);
把10个double 大小的值拷贝进earnings数组中。
实例
struct book {
char title [MAXTITLE];
float value;
};
此时并未让编译器分配空间
结构成员运算符----点(.)访问结构种的成员
&book.value
:点的优先级大于&
实例
/*结构的初始化方式*/
#include
#define MAXTITLE 41
/*book 结构体声明*/
struct book {
char title[MAXTITLE];
float value;
};
int main (void)
{
struct book gift = {.value = 11.99, .title = "Swing", 11.98, 11.90}; /*11.90哪儿去了?*/
printf("title = %s value= %f", gift.title, gift.value);
return 0;
}
警告⚠
structInitializer.c:14:66: warning: excess elements in struct initializer //元素溢出
struct book gift = {.value = 11.99, .title = "Swing", 11.98, 11.90};
^~~~~
structInitializer.c:14:66: note: (near initialization for 'gift')
title = Swing value= 11.980000
/*结构数组*/
#include
#define MAXTITLE 41
const int MAX = 5;
struct book {
char title [MAXTITLE];
float value;
};
int main (void)
{
struct book libry[MAX];
libry; // 一个book结构的数组
libry[2]; // 第三个数组元素,该元素是book结构
libry[2].title; // 一个char型字符数组,libry[2]内的成员title
libry[2].title[4]; // 一个char型字符
return 0;
}
him -> value;
(*him).value;
struct book *it;
it -> value; // 与&libry[0].value == (*it).value
it++; // 指针的特点
注意:允许把一个结构赋值给另一个结构,但数组无法赋给另一个
Q:数组相互之间如何赋值?
A:使用结构体包装数组!!!
声明
struct book getInfo(struct book); // 返回:book型结构;形参:book型结构
指针
结构本身
老版本
只能传指针字符串存储声明的两种形式:
①字符数组
②字符指针
#define LEN 20
struct names { // ①字符数组(简单)
char first[LEN];
char last[LEN];
};
struct pnames { // ②字符指针,滥用会导致严重后果
char * first;
char * last;
};
int main (void)
{
struct pnames ptr;
char temp[LEN];
ptr -> first = (char *)malloc (strlen(temp) + 1); // malloc函数进行内存分配
free(ptr -> first); // 释放内存资源
return 0;
}
最后一个成员
空
struct flex
{
int count;
double average;
double scores[]; // 伸缩数组成员
};
struct plex *pf; // ①声明一个指针结构的指针
pf = malloc(sizeof(struct flex) + 5 * sizeof(double)); // ②请为一个结构和一个数组分配存储空间
pf->count = 5;
pf->scores[2] = 18.2; // 可以使用
带伸缩数组的结构
赋值
和拷贝
(只能拷贝除伸缩数组外的成员)union hold {
int digit;
double bigfl;
char letter;
}; // 和结构的声明一致
如果14.5.1声明的是个结构
,那它可存储一个int型、一个double型和一个char型
联合
:只能存储一个,要么存一个int,要么一个double或者char
14.5.1的联合中,声明一个hold联合,仅会分配一个double数据(8字节)大小
的内存空间
后面会覆盖前面的赋值语句
联合命名
struct data {
char make[15];
int status;
union {
struct owner ow;
int x;
};
}
枚举类型的目的:提高程序的可读性、可维护性
枚举类型是整数类型
/*枚举类型测试程序*/
#include
enum spectrum {red, orange, yellow, green, blue, violet}; /*枚举类型是整数类型*/
int main (void)
{
enum spectrum color;
color = blue;
if (color == blue)
puts("It is blue...");
for (color = red; color <= violet; color++) /*C语言中能够使用++,但是C++不允许*/
printf("%d\n", color); /*枚举类型是整数类型 %d接收*/
return 0;
}
演示
It is blue...
0
1
2
3
4
5
枚举类型可以指定整数值
enum levels {cat, dog = 10, cow, rat, horse}; /*注意其中cat == 0, dog, cow, rat == 10, 11, 12*/
typedef
:为某一类型(不能是值)自定义名称,通常大写
typedef
char * STRING; // 没有typedef:编译器把STRING识别为一个指向char的指针变量
typedef char * STRING; // 有typedef:编译器把STRING解释成一个类型的标识符,该类型指向char的指针
STRING name, sign;
// 注意:相当于:
char * name, * sign;
#define
#define STRING char *
STRING name, sign;
// 注意:相当于
char * name, sign;
#define bool _Bool
#define true 1
#define false 0
/*tyoedef修饰结构*/
#include
typedef struct {
int x;
int y;
} rect;
int main (void)
{
rect r1 = {3, 4};
printf("r1.x = %d, r1.y = %d\n", r1.x, r1.y);
rect r2;
printf("r2.x = %d, r2.y = %d\n", r2.x, r2.y);
r2 = r1;
printf("r2.x = %d, r2.y = %d\n", r2.x, r2.y);
return 0;
}
演示
r1.x = 3, r1.y = 4
r2.x = 1643472, r2.y = 0
r2.x = 3, r2.y = 4
注意
rect r1 = {3, 4};
rect r2;
// 可以被翻译为:
struct {int x; int y;} r1 = {3, 4};
struct {int x; int y;} r2;
表14.1 声明时可使用的符号
符号 | 含义 |
---|---|
* | 表示一个指针 |
() | 表示一个函数 |
[] | 表示一个数组 |
下面是一些复杂的声明:
/*
[]是结合律优先级高于*符号
先指针--->是指针
先数组--->是数组
*/
int board[8][8]; // 声明一个内含int数组的数组
int ** ptr; // 声明一个指向指针的指针,被指向的指针指向int
int * reisks[8]; // 声明一个内含8个元素的数组,每个元素都是指向int的指针---指针数组
int (* resks)[8]; // 声明一个指向数组的指针, 该数组内含8个int类型数据---指向数组的指针
int * off[3][4]; // 声明一个内含3x4个元素的数组,每个数组都是指向int的指针
int (* uff)[3][4]; // 声明一个指向数组的指针,该数组内含3x4个int类型数据
int (* uof[3])[4]; // 声明一个内含3个指针元素的数组,每个指针都指向一个内含4个int类型元素的数组
void (*pf) (char *); // pf 是一个指向函数的指针,参数列表是(char *),返回值是void
void *pf (char *); // pf 是一个返回字符指针的函数
void ToUpper(char *);
pf = ToUpper; // 允许
Page415
警告:本文档仅用于个人学习!!!