关键字:void sizeof(运算符,它能告诉你某样东西在储存器中占多少字节。例:sizeof(int)返回4,sizeof("Turtles!")返回9,其中包含8个字符外加\0结束字符。)
1.puts可以输出,与printf用法类似。
/*假设人名小于20个字符*/
char ex[];
puts("输入男友的名字:");
scanf("%19s", ex);
printf("亲爱的%s,我们分手吧。\n", ex );
3.switch。。。case选择的一种用法。
例:
char suit = 'H';
switch (suit) {
case 'C':
puts("梅花(Clubs)");
break;
case 'D':
puts("方块(Diamonds)");
break;
case 'H':
puts("红心(Hearts)");
break;
default:
puts("黑桃(Spades)");
}
/*
这里写注释
*/
#include <stdio.h> //包含的外部代码。
int main()
{
int decks;
puts("输入有几副牌");
scanf("%i", &decks);
if (decks <){
puts("无效的副数");
return ;
}
printf("一共有%i张牌\n", (decks * ));
return ;
}
main()函数的返回类型是int。当计算机在运行程序时,它需要一些方法来判断程序是否运行成功,计算机正是通过监察main()函数得返回值来做到这一点。如果让main()函数当回0,就表明程序运行成功;如果让它返回其他值,就表示程序再运行时出了问题。
把数字4赋值给变量。有趣的是表达式“x = 4”
本身也有一个值。为什么说这个东西很有用呢?因为你可以用它来做一些很酷的事情,比如你把多条赋值语句链在一起写: y=(x =
4);这行代码同时将x和y的值设为了4。事实上,可以去掉括号,缩短代码的长度:y = x = 4;
你经常会在需要给多个变量赋相同的值的代码中看到链式赋值。
printf("%s说计数是%i","阿星",21);
!当调用printf()时,可以包含任意数量的参数,但确保每个参数都要有一个对应的%格式符。
因为在类linux操作系统中,运行程序必须指定程序所在的目录,除非程序的目录已经列在了PATH环境变量中。
通常应该用双引号来定义字符串。用双引号定义的字符串叫字符串字面量(string literal),比起字符数组,它输入起来也更方便。
15.字符串字面值和字符数组有没有区别?//p12
只有当给出的两个条件同时为真时,与运算(&&)的结果才为真。例:
if((dealer_up_card == ) && (hard == ))
if (cupcakes_in_fridge || chips_on_table)
eat_food();
如果第一个条件为真,计算机就不会自找麻烦地去计算第二个条件,因为它知道只要第一个条件为真,整个条件也一定为真。
if(!brad_on_phone)
answer_phone();
!在C语言中,布尔值是用数字表示的。对C语言来讲,数字0代表假的值。那什么数字代表真呢?任何不等于0的数字都将被当成真处理,因此下面的C代码也没有错。c99中允许程序使用true和false关键字,但编译器还是会把他们当作1和0这两个值来处理。
int people_moshing = ;
if (people_moshing)
take_off_glasses();
====================================
18.为什么不能只写一个|和&?
也不是不行。&和|操作符总是计算两个条件,而&&和||可以跳过第二个条件。
19.那还要|和&干什么呢?
对逻辑表达式求值只是他们的一个用处,他们还能对数字的某一位进行布尔运算。
20.那是什么意思?
A:
#include <stdio.h> int main()
{
int card = ;
if (card >)
card = card - ;
if (card < )
puts("小牌");
else {
puts("Ace!");
}
retutn ;
} B:
#include <stdio.h> int main()
{
int card = ;
if (card >){
card = card - ;
if(card < )
puts("小牌");
else
puts("Ace!");
}
return ;
}
C:
#include <stdio.h> int main()
{
int card = ;
if(card > ){
card = card - ;
if (card < )
puts("小牌");
} else
puts("Ace!");
return ;
} D:
#include <stdio.h> int main()
{
int card = ;
if(card > ){
card = card - ;
if (card < )
puts("小牌");
else
puts("Ace!");
return ;
}
A:编译成功。程序显示“小牌”,但程序不能正确工作,因为else匹配了错误的if.
B: 编译成功。程序什么也没有显示,它不能正确工作,因为else匹配了错误的if.
C:编译成功。程序显示“Ace!”,正确!
D:编译失败。因为花括号不匹配。
22.switch语句只能检查变量吗?它能检查值吗?
能,switch语句仅仅检查两个值是否相等。
23. 我能在switch语句中检查字符串吗?
24.do while
while循环还有一种形式,它总是在循环体运行后才检查循环的条件,也就是说循环体至少会被执行一次,我们叫它do ...while循环。例:
do{
/*买彩票 */
} while(have_not_won);
======================================
25.所有循环的结构都相同。
首先为循环准备变量,其次在每一轮的循环前检查条件,最后在循环末尾更新计数器或实现类似功能。
例:
int counter = ; //这是循环启动代码
while (counter < ) { //循环条件
printf("%i个枣\n", counter);
counter++; //这是循环更新代码,它用来在循环体的末尾更新计数器。
}
==========================================
26.因为这个模式是通用的,C语言的设计者就创造了for循环。例:
int counter;
for (counter = ; counter < ; counter++) {
printf("%i 个枣\n", counter);
} // 循环体中只有一行代码,可以去掉花括号。
for循环可以减少代码的行数
===============================
27.用break语句退出循环
例:
while (feeling_hungry){
eat_cake();
if (feeling_queasy){
/* 从while循环中跳出 */
break;
}
drink_coffee();
}
!break语句可以直接退出当前循环,跳过循环体中break之后的所有语句。 break非常有用,因为它有时是结束循环最简单有效的方法,但应该避免滥用break,因为他们会降低代码的可读性。!break语句可以用来退出循环语句和switch语句。使用break时看清你在哪里,并不是所有地方都能够使用break。
如果想跳过循环体的其余部分,然后回到循环的开始,那么continue语句就是你的最佳伴侣。例:
while (feeling_hungry) {
if (not_lunch_ye) {
/* 回到循环条件 */
continue; //"continue" 带你回到循环的条件
}
eat_cake ();
}
break 不能从if语句中退出。
1990
年1月15日,AT&T的长途电话系统死机,造成6万人无法使用电话服务。起因是一个负责写电路交换部分C代码的开发人员企图用break从if
语句中退出,但break不能从if语句中退出。相反,程序跳过了诊断代码,引起了这个bug,令7千万次电话呼叫在9个多小时内无法接通。。。
29.如果我创建了一个void函数,是否就意味着它一定不能有return语句?
你还是可以包含return语句,但编译器很可能会产生一条警告消息。在void函数中的return语句有时可以用来提前退出函数。
30.链式赋值:
在
C语言中,几乎每样东西都有返回值。例如下面这条语句:x = 4; 他把数字4赋值给变量。有趣的是表达式“x = 4
”本身也有一个值,这个值是4,即赋给x的值。为什么说这个东西很有用呢?因为你可以用它来做一些很酷的事情,比如把多条赋值语句链在一起写:y =
(x = 4); 这行代码同时将x和y的值设为了4。事实上,可以去掉括号,缩短代码的长度:y = x = 4;
你经常会在需要给多个变量赋相同值的代码中看到链式赋值。
31.块语句被{和}包围
32.count++表示技术加1,count--表示计数减1
储存器和指针:
1.指针就是储存器中某条数据的地址。
2.指针做了两件事:避免副本和共享数据。
3.如果在函数中声明变量,计算机会把它保存在一个叫栈的储存器区段中;如果你在函数以外的地方声明变量,计算机则会把它保存在储存器的全局变量段。
4.航行问题:
例:
#include <stdio.h> void go_south_east(int lat, int lon)
{
lat = lat - ; //纬度减小
lon = lon + ; //经度增加
} int main()
{
int latitude = ;
int longitude = -;
go_south_east (latitude,longitude);
printf("停!当前位置:[%i, %i]\n", latitude, longitude);
return ;
}
程序开始时船的位置是【32,-64】,如果它向东南方向航行,船的新坐标将是【31,-63】,前提是代码正确工作。。。
!c语言传递参数的值
c语言调用函数的方式是导致这段代码不能正确工作的原因。
a.一开始,main()函数有一个叫longitude的局部变量,它的值是32.
b. 但计算机调用go_south_east()函数时,他将变量longitude的值赋值给了参数lon,这只是一个赋值的过程,从变量longitude到变量lon。也就是说,当你调用函数时,作为参数传递的不是变量,而只是变量得值。
c.但go_south_east()函数修改了lon的值时,函数只是修改了本地的副本,也就是说程序返回mian()函数时,变量longitude中保存的还是它原来的值32.
5。得到了变量的地址,就需要把它保存在某个地方。为此,需要指针变量。
6.指针只是一个保存储存器地址的变量。
7.&运算符可以找到变量的地址。
8.*运算符可以读取储存器地址中的内容。*运算符还可以设置储存器地址中的内容。
9.数组变量:
char
quote[] = "Cookie make you fat";
quote是数组变量,计算机会为字符串的每一个字符以及结束字符\0在栈上分配空间,并把首字符的地址和quote变量关联起来,代码中只要出现这个
quote变量,计算机就会把它替换成字符串首字符的地址。
10.通过函数传递参数,函数只能接受传递过来参数的副本。
11.为什么指针有类型?
因为指针可以运算,对char指针加1,指针会指向储存器中下一个地址,因为char就占1字节。如果int指针加1,int通常占4字节,编译后的代码就会对储存地址加4.指针之所以有类型,是因为编译器在指针算术运算时需要知道加几。
12.一次scanf输入多个参数,例:scanf("%19s %19s", first_name, last_name);
13.scanf()会导致缓存区溢出
例:
char food[];
printf("Enter favorite food:");
scanf("%s",food);
printf("Favorite food: %s\n", food);
程 序运行崩溃,因为scanf()在写数据时超过了food数组的尾部。如果忘了限制scanf()读取字符串的长度,用户就可以输入远远超出程序空间的数 据,多余的数据会嗅到计算机还没有分配好的储存器中。如果运气好,数据部但能保存,而且不会有任何问题。但缓存溢出很有可能会导致程序出错,这种情况通常 被称为段错误或abort trap,不管出现什么错误信息,程序都会崩溃。
14.除了scanf()还可以用fgets()
char food[];
printf("Enter favorite food:");
fgets(food,sizeof(food),stdin);
#include <stdio.h> int main()
{
char *cards = "JQK";
char a_card = cards[];
cards[] = cards[];
cards[] = cards[];
cards[] = cards[];
cards[] = cards[];
cards[] = a_card;
puts(cards);
return ;
}
字符串字面值不能更新,指向字符串字面值的指针变量不能用用来修改字符串的内容:
char *cards = "JQK"; //不能用这个变量修改这个字符串。
也不能用 card = "JQK"去修改,原因在上一张15点。
但你可以用字符串字面值创建一个数组,就可以修改了:
char cards[] = "JQK"; 或 card = { "J","Q","K"};
这是有C语言使用储存器的方式决定的 //P73
16.cards[]还是*cards? //p74也可以不立即赋值,但要写出数组的大小,例:char cards[5];cards就是一个数组。
17.p75
18.
为了从此避免这个错误,可以不再将char指针设置为字符串字面值,像这样:char *s = "Some
string";但是把指针设为字符串字面值又没有错,问题出在你试图修改字符串字面值。如果你想把指针射程字符串字面值,必须确保使用了const关键
字:
const char *s = "some string";这样一来,如果编译器发现有代码试图修改字符串,就会提示编译错误:
s[0] = 'S';
monte.c:7: error: assignment of read-only location
19.const到底是什么意思?他能让字符串变成只读吗?
加不加const,字符串字面值都是只读的,const修饰符表示,一旦你试图用const 修饰过的变量去修改数组,编译器就会报错。
20.我还是不理解,为什么数组变量不保存在储存器中?既然它存在,就应该在某个地方,不是吗?
程序在编译期间,会把所有对数组变量的引用替换成数组的地址。也就是说在最后的可执行未见中,数组变量并不存在。既然数组变量从来不需要指向其他地方,有何没有其实都一样。
21.每当我把一个新数组设为字符串字面值,程序实际上会复制字符串字面值的内容吗?
这取决于编译器,最后的机器代码既有可能吧整个字符串字面值的内容复制到数组,也有可能程序会根据生命设置每个字符的值。
22.你老是在说 “声明”,他是什么意思?
声明是一段代码,他是称某样东西(变量或函数)存在;而定义说明他是什么东西。如果在声明了变量的同时将其设为某个值(例如 int x = 4;),这段代码即是声明又是定义。
23.为什么scanf()要被称为scanf()?
scanf()其实表示“scan formatted”,它用来扫描带格式的输入。
22.P80
字符串
1.string.h库中的函数:
strchr() 在字符串中查找字符;strcmp() 比较字符串;strstr()在字符串中查找字符串;strcpy()复制字符串;strlen()返回字符串的长度;strcat() 连接字符串
2.如果使用mac或linux的计算机,可以在命令行中查看string.h中每一函数的详细介绍,假如想查看strstr()函数,可以输入: man strstr
3.为什么要把数组定义成tracks[][80]而不是tracks[5][80]?
也可以这样定义,但编译器知道列表有5项,所以你可以省略5,写成[]。
4.既然如此,为什么不直接写tracks[][]?
5。为什么我一定要把find_track()放在main()之前?
在调用函数前,编译器需要知道两件事,函数就首什么参数以及函数的返回类型是什么。
6.如果我把main()放到前面会怎么样?
你会得到几个警告。
7.能够倒过来显示字符串的函数://P97
8.重点:指针的数组。
结构、联合、位置段
1.const char *表述传递字符串字面值。
2.struct是structured data type(结构化数据类型)的缩写。例:
struct fish {
const char *name
const char *speces;
int teeth;
int age;
}
======================
3.fish结构会保存字符串吗?
在这个例子中不会,这里的fish结构中只保存了字符串指针,也就是字符串的地址,字符串保存在储存器中其他位置。
4.但还是可以把整个字符串保存在结构中吧?
对,只要把字符串定义成字符数组就行了,像这样char name[20];.
5.既然snappy是数组指针,就可以像这样访问它的第一个字段:
例:
struct fish snappy = {"snappy","piranha",,};
printf("Name = %s\n",snappy[]);
如果想访问数组元素那样读取结构字段,会得到编译错误,不可以这样做。尽管结构可以向数组那样在结构中保存字段,但读取时只能按名访问。可以使用“.”运算符访问结构字段。
6.数组变量就是一个指向数组的指针,那么结构变量是一个指向结构的指针吗?
不是,结构变量是结构本身的名字。
7.但我定义结构时,必须使用struct关键字,定义变量时还要再用一遍,有什么更简单的方法吗?
用typedef为结构命名:
当创建内置数据类型变量时,只要写int 或double就行了,但每次创建结构变量时,不得不加上struct关键字。
在c语言中可以为结构创建别名,你只要在struct关键字前加上typedef,并在右花括号后写上类型名,就可以在任何地方使用这种新类型。例:
typedef struct cell_phone {
int cell_no;
const char *wallpaper;
float minutes_of_charge;
} phone;
...
puone p = {, "sinatra.png",1.35};
=======================
8.什么是匿名结构?
9.(*t).age和*t.age是完全是两个不同的表达式。
10.(*t).age 和 t->age这两个表达式含义相同。
可以像创建其他类型的指针那样创建结构指针。
12.同一类事物,不同数据类型:假如想记录某样东西的“量”,既可以用个数,也可以用重量,或者用容积。如果能够定义一种加“量”的数据类型,然后根据特定的数据决定要保存个数、重量还是容积。在c语言中,可以用联合做到这点。例:
typedef union {
short count;
float weight;
float volume;
} quantity;
联合看起来很像结构,但用的是union关键字。所有字段将保存在同一个地方。数据类型不同,但都表示“量”。
a.C89方式:如果联合要保存第一个字段的值,就可以用C89表示法,只要用花括号把值括起来,就可以把值赋给联合中第一个字段。例:quantity q = {4}
b.指定初始化器:指定初始化器(designated initializer)按名设置联合字段的值:quantity q = { .weight = 1.5 };
c.“点”表示法:第三种设置联合值的方法是在第一行创建变量,然后在第二行设置字段的值:
quantity q;
q.volume = 3.7;
====================
14."指定初始化器"也可以用来设置结构字段的初值。如果结构有很多字段,但你只想为其中某些字段设初值,“指定初始化器”就非常有用。同时它还能够提高代码的可读性:例:
typedef struct {
const char *color;
int gears;
int height;
} bike;
bike b = {.height=, .gears=}; 将设置height和gear字段,但不设置color字段。
=====================
15.你可以在联合中保存任何字段的值,这些不同类型的值保存在储存器中相同的位置。。。既然如此,你怎么知道我保存的是float还是short?要是我保存了float字段,却读取了short字段呢?
好问题:可以在联合中保存各种可能的值,但保存以后,就无法知道它的类型。编译器不会记录你在联合中设置或读取过那些字段。我们完全可以设置一个字段,读取另一个字段,但有时这会造成很严重的后果。
16.你需要某种方法记录我们在联合中保存了什么值。C 程序员常用觉得一种技巧是创建枚举。
17.枚举变量保存符号:
有时你不想保存数字或文本,而是想保存一组符号。如果你想记录一周中的某一天,只想保存MONDAY,TUESDAY,WEDNESDAY...这些符号。你不需要保存文本,因为一共只有7种不同的取值。这就是为什么要发明枚举的原因。定义例子:
enum colors { RED, GREEN, PUCE };
enum colors favorite = PUCE;
enum colors 类型定义的变量只能设为列表中的某个关键字
==========================
18.位置段:例:
typedef struct {
unsigned int low_pass_vcf:;
unsigned int filter_coupler:;
unsigned int reverb:;
unsigned int sequential:;
...
} synth;
19.为什么C语言不支持二进制字面值?
因为二进制字面值占了很大空间,而且十六进制通常写起来更快。
20.位置段就是为了节省空间吗?
不仅仅是为了节省空间,如果需要读取底层的二进制信息,位字段就会非常有用。
21.枚举保存符号而不是字符串。
数据结构与动态储存
1.为了保存可变数量的数据,需要一样比数组更灵活的东西,即链表。
2.链表是一种抽象数据结构。链表是通用的,可以用来保存很多不同类型的数据,所以被称之为抽象数据结构。
3.链表中的每个结构都需要与下一个结构相连。如果一个结构包含一个链向同种结构的连接,那么这个结构就被称为递归结构。
4.链表:例
typedef struct island {
char *name;
char *open;
char *closes;
struct island *next; //在结构中保存了一个指针,指向下一座岛。
} island;
island.
5.在C语言中,null的值实际上为0,null专门用来把某个指针设为0.
6.其他语言,比如java,有内置链表,C语言有内置数据结构吗?
c语言没有内置数据结构,你必须自己创建它们。
7.如果我想快速地插入数据,就需要链表,但如果我想直接访问元素,就应该用数组。
8.P276 为什么要用动态储存。
9.malloc()
获取空间,free()释放储存器。申请储存器的函数叫:malloc(),是memory
allocation(存储器分配)的意思。malloc()接收一个参数:所需要的字节数。通常你不知道确切的字节数,因此malloc()经常与
sizeof运算符一起使用,像这样:malloc(sizeof(island));
10.P285 strdup()函数
11.p286 中的“这里没有蠢问题”
12.exit()系统调用是结束程序的最快方式。完全不用操心怎么返回主函数,直接调用exit(),你的程序就会灰飞烟灭。exit()括号中的数字是返回主函数的状态码。
高级函数
1.void *指针可以指向任何数据类型。
2.int a = * (int *)score_b; (int *) 把void指针转化为整形指针。
3.char ** P331页
4.重复利用函数中的公共部分,使用函数指针可以实现。
使用宏可以创造出可变参数的函数,可以解决一些难题。
5.#defined 宏定义的使用,可以看看二级c参考资料中的使用。
1.system()函数会编译到我的程序中吗?
答:不会,和所有系统调用一样,system()函数不在你的程序里,而在操作系统中。
2.system()
函数会带来很多安全问题;当调用system()函数时,操作系统必须解释命令字符串,然后决定那些运行程序和怎样运行。问题就在“操作系统需要解释字符
串”上,你已经看到这有多么容易出错。要想解决这个问题就必须消除歧义,明确告诉操作系统你想运行哪个程序,这就是exec()函数的用处。
3.exec()函数;替换当前进程。你可以告诉exec()函数要使用哪些命令行参数和环境变量。新城需启动后PID和老程序一样,就像两个程序接力跑,你的程序吧进程交给了新程序。