嗨翻C语言笔记(一)
对自己狠一点,逼自己努力,总有一天你会感谢今天的自己!
C语言不支持现成的字符串, 只能用数组表示.
& (and)运算, 即两个数的每个二进制位都进行比较, 对等位均为1时为1, 否则为0. 比如在计算机网咯笔记中有 ip和子网掩码对比获取子网 的印象过程中:
C 的数据类型
浮点型: float, double
整数型: char, int, short,long
[C中char以字符编码保存,比如a保存为 65 ]
所以 6 & 4 的结果是4, 这是怎么求得的呢?
6 转成二进制是 110
4 转成二进制是 100
两个数求 & 后的值是 100 转换成十进制就是4, 因此结果就是4.
printf() //用于格式化输出
%s : 字符串
%i : 整数
%p : 将地址以10禁止的格式输出
在C中指针的长度占用8个字节. (32位占4个字节,64位系统占8个字节)
* 运算符 和 &运算符的区别:
&
@1. 后接变量名,表取址运算符,用于获取该变量的内存地址
@2. 做二进制运算表 and 运算,用于返回两个比较数二进制对等位上同时为真的位, 比如 6 & 4 返回 4.
*
@1. 声明变量时加它仅表指针类型
@2.使用时在变量前加它,表取值运算符,用于获取该内存地址对应的值
因此
int *p; //声明时前面带 * , 进表示他是指针类型
*p = 5; //该指针p没有任何引用地址,就妄图赋值,报错的, 它是野指针, 必须先有内存地址后才能赋值.
int a = 10;
p = &a; //&a是取址,返回给p变量的是一个地址值,
*p = 14; //单个 *p 表取值, 整个表达式的意思是先取值然后给赋值14
再来深入理解下面的例子:
char a[] = "hello"; //实质上等于 char a[] = {'h', 'e', 'l', 'l', 'o'};
char *p;
p = &a; //p获得了字符串的第一个元素的地址
printf("p=%p\n", p);
printf("p+1=%p \n", (p+1) ); //打印第二个元素的地址
printf("*(p+1)=%c \n", *(p+1) ); //打印第二个元素的值, %c表字符, %s表字符串,不要用错.
输出为:
p = 0x7fff5a563786
p+1 = 0x7fff5a563787 86,87结尾充分说明一个字符占用一个字节
*(p+1)=e
上面字符串地址 p 和 p+1 只差一位,那是因为它是字符, 如果是整型,那么这个地址值就要差4位了, 看下面:
int a[] = {1,2,3,4};
int *p = NULL;
p = &a; //p获得了字符串的第一个元素的地址
printf("p=%p\n", p); //打印第一个元素的地址
printf("p+1=%p \n", (p+1) ); //打印第二个元素的地址
printf("*(p+1)=%d \n", *(p+1) ); //打印第二个元素的值
看输出:
p =0x7fff5fad8770
p+1=0x7fff5fad8774
*(p+1)=2
存储区的结构:
栈区 --局部变量
堆区
全局量区 --所有函数外,包括main函数
常量区域
代码段区域
14:38 冷水
vs 3. sizeof() 可用于获取变量的占用长度或类型长度
@1. 类型:
sizeof(int) ; //返回4, 表整型占用4个字节
@2.占用字节:
char b[] = "hello world";
printf("%d\n",sizeof(b) ); //使用数组变量名, 返回12,其中加空格是11个,最后一个 \o 数组结束符
@3. 指针类型
check(b); //输出8, 表指针的长度是八个字节
void check(char msg[]) {
//当参数传递的时候,用sizeof()把数组当成了第一个元素的指针地址[指针类型],因此输出的是指针的长度
printf("sizeof value:%d", sizeof(msg));
}
vs 4. 计算机会为字符串的每一个字符(包括空格)以及结束符\0 在栈上分配空间,并把首字符的地址和变量名关联起来, 只要出现这个数组变量名, 计算机就会把他替换成字符串首字符的地址,因此数组变量就好比一个指针.
printf("%s\n", b ); //输出 "hello world"; 当字符串
printf("%p \n", b); //输出 0x7fff5a63878 当地址
vs 5. 特殊的变量字符串 - 在C语言中字符串其实是char数组
char *cards = "jqk"; //"jqk"是存放在常量区的,只读
printf("cards=%p\n", cards); //cards 获得了常量字符串的第一个元素的指针
printf("cards=%p\n", cards+1); //输出第二个元素地址
printf("cards=%p\n", cards+2); //输出第3个元素地址
printf("*cards=%c\n", *cards); //输出第1个字符元素
printf("*cards=%c\n", *(cards+1)); //输出第2个字符元素
printf("*cards=%c\n", *(cards+2)); //输出第3个字符元素
输出:
cards =0x10475ef30
cards+1 =0x10475ef31
cards+2 =0x10475ef32
*cards =j
*cards+1=q
*cards+2=k
如果妄图修改 *cards 的值都会报错的, 因为它是常量的, 要修改只能把字符串转成数组, 那么它就存储在全局变量区或栈区,就可以进行修改了,如下:
char cards2[] = "hello"; //字符串在栈区是当做数组存储的 相当于 char cards2[] = {'h','e','l','l','o'}
*(cards2 + 1) = 'g';
printf("cards2=%s \n", cards2);
//上面的切记写成 char *cards2[] = "hello"; //没有这样的, 指针是一个定长地址值,一般表某类型的第一个地址, 你用 *cards[] 表示是一个指针的数组,指着N个元素, 而你的值却是一个字符串, 除非你写成 char *cards2[] = {"hello", "world"};
因此 char *cards2 = "hello"; 相当于 char *cards2 = {'h','e','l','l','o'}
因此 *cards2 表示第一个字符 'e'
和 char *cards3[] = ["hello"] 是不一样的.
而 *cards3 表示的是第一个字符串 "hello"
使用 cards2时具体看语义是打印整个数组还是打印地址值, 如下:
printf("cards2=%p", cards2); //输出 0x7fff5f06e75a, %p 表要输出的是 栈区内存地址值
printf("cards2=%s", cards2); //输出 "hello", %s 表要输出的是字符串字面值
标准库:
stdio.h 提供了标准输入/输出函数, 如 printf(), scanf(), fgets()...
string.h 提供了字符串的各种操作函数.
vs 7. 指针运算术
例如编写一个翻转某字符串的函数:
void print_reverse(char *s) {
size_t len = strlen(s);
char *t = (s + len) -1 ; //获取该字符串的最大的指针地址
while ( t >= s ) {
printf("%c\n", *t );
t = t - 1;
}
}
vs 8. 数组指针
char *arr[4] = {"hello", "world", "good", "night"};
其中 arr 表示第一个元素的地址, *arr 表示第一个元素的取值即"hello"
那么 *(arr + 1) 的取值就是"world", 以此类推 ...
*(p+i) 的写法等价于 p[i]
那么 arr[1] 等价于写 *(arr +1) , 于是 arr[1] 也等于 "world"
vs 9.
./geo2json < gpsdata.csv > output.json
在屏幕上等待用户输入
在没使用导入文件前, 每条数据都要在屏幕上使用 scanf("%f, %f, %79[^\n]", &latitude, &longitude, info) 函数等待用户输入,
其实屏幕输入和 "< gpsdata.csv" 导入文件是一样的, 都是标准输入stdin, 因此可用后者取代前者.
前半部分获取标准输入到程序中去, 然后程序的标准输出到output.json 文件去
一般情况下使用
printf() 默认会把文本发送到标准输出stdout 去, 假如你要实现正常的发送到标准输出, 异常的发送到标准错误stderr去, 该咋办???
"<" 标准输入 stdin
">" 的作用是重定向标准输出 stdout
"2 > " 是重定向标准错误. stderr
假如你原来的
printf( "invalid latitude. %f\n", latitude); 该类错误信息不用弄到标准输出去, 否则会被捕获到 > output.json去的, 你该咋办???
使用 fprintf(stderr, "invalid latitude. %f\n", latitude); //表示打印到标准错误去, 因此不会被 "> output.json" 进去, 而是输出到屏幕上.
fprintf(); 第一个参数用于指定 stdin, stdout, stderr中的一种类型.
vs 10. 根据用户输入的参数来刷选, 并把数据写入指定的文件中去
比如命令行执行 ./categorize mermaid mermaid.csv Elvis elvis.csv the_rest.csv
- mermaid : 第一个要过滤 argv[0]
- mermaid.csv: 第一个要过滤的数据mermaid 写入这个文件 argv[2]
- Elivis 第二个过滤argv[3], elvis.csv第二个要写入的文件 argv[4]
- the_rest.csv 其余的数据保存在这里. argv[5]
- ./categorize hello hello.csv good good.csv today.csv
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
char line[80];
if(argc != 6) {
fprintf(stderr, "you need to give 5 arguments!\n");
return 1;
}
//读取的文件
FILE *in = fopen("in.csv", "r");
//将要写入的三个文件
FILE *file1 = fopen(argv[2], "w");
FILE *file2 = fopen(argv[4], "w");
FILE *file3 = fopen(argv[5], "w");
while (fscanf(in, "%79[^\n]\n", line) ==1 ) {
if (strstr(line, argv[1]) ) //搜索到这个字符就写入这个文件
fprintf(file1, "%s\n", line);
else if (strstr(line, argv[3]))
fprintf(file2, "%s\n", line);
else
fprintf(file3, "%s\n", line);
}
fclose(file1);
fclose(file2);
fclose(file3);
return 0;
}
vs 11 命令行配置选项
- 像 ps -ef 这类命令的-ef 选项是怎么实现的 ???
- C语言处理命令行选项的库函数叫getopt(), 每调用一次都会返回命令行中下一个参数.
比如命令 ./rocket -e 4 -a braisella tokyo londo
getopt(argc, argv, "ae:") //其中 ae表示两个选项是有效的, ":" 表示e选项后还需一个参数.
选项之后的":"冒号表该选项需要接收一个参数
optind 变量保存了getopt()函数从命令行读取了几个选项,
下面这两行用来跳过已经读取过的选项.
argc -= optind;
argv += optind;
跳过之后argv的数组将变成
braisella tokyo londo
示例: 外卖披萨, 指定接收时间和披萨厚薄度
./pizza -d now blue gree red -t
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char *argv[]){
char *delivery = ""; //不能定义为字符串,字符串一旦定义内容无法再更改, 只能定义为指针.
int thick = 0;
int count = 0;
char ch ; //下一个参数
while((ch = getopt(argc, argv, "td:")) != EOF ) {
switch (ch) {
case 'd':
delivery = optarg;
break;
case 't':
thick = 1;
break;
default :
fprintf(stderr, "unknow option: '%s'\n", optarg);
return -1;
}
}
argc -= optind;
argv += optind;
if(thick)
puts("Thick pizza ...\n");
if ( delivery[0] )
printf("to be delivery %s \n", delivery);
for (count =0; count < argc ; count++){
printf("参数 %i 的值是:%s\n", count, argv[count]);
}
return 0;
}
vs 12. 为啥把一个很大数保存到小的数据类型里数据溢出的值是负数???
因为数字以二进制保存, 比如 100 000 的int型二进制位是:
0001 1000 0110 1010 0000
而一个 short类型只能保存2个字节即 2^16 = 65536 大小
因此 把 100 000 塞进short里时只能保存2个字节,所以只保存了数字右半边.
即:
1000 0110 1010 0000
而二进制的第一位是有符号数为,值为1表负数, 因此转成十进制后是:
-31072
vs 13. 如果两个整数相除转成浮点数的话,余数会被舍掉,
- 因此要输出正确浮点数应先把整数保存到float变量里或使用类型显式转换.
int x = 7;
int y = 2;
float z = x / y; //输出的是3.0000, 而不是3.5000
正确的姿势应该是:
分子/分母同时转换
float z = (float) x / (float) y; //输出 3.50000
或分子/分母其中一个转换即可
float a = x/(float)y; //输出 3.50000
float b = (float)x/y; //输出 3.50000
vs 14. 可以在数据类型前加long, 让它变长
比如 long double d;
vs15. 看到编译器返回"conflicting type for '某函数名' "的错误.
这是由于被调用的函数未写在调用语句前,因此编译器不知该函数的返回值是啥, 于是首先假定该函数的返回值为int, 最后找到函数的定义处发现类型不是int的时候就报上面的错误了. [编译器在想: 你怎么跟我之前记录的int返回值不一样呀??? 于是抛出错误]
为避免这种顺序导致的编译错误, 方法有2:
@1. 在调用前声明一个空方法体.
比如: float add_with_tax(float f); //没有方法体
完整如下:
#include <stdio.h>
float sumTotal(float i); //声明
int main(){
//....
printf("i=%f\n",sumTotal(5.5) );
}
float sumTotal(float i) {
return i * 2;
}
@2. 把方法声明体放入一个 *.h 头文件, 然后引入进来
比如新建一文件func.h 然后里面的代码如下:
float sumTotal(float i);
在调用的源文件头部引入上面的func.h, 如下:
#include <stdio.h> //引入标准库目录下的stdio.h头文件
#include "/usr/local/func.h" //引入自定义路径下的头文件
int main(){
printf("i=%f\n",sumTotal(5.5) );
}
float sumTotal(float i) {
return i * 2;
}
PS: 编译器看到尖括号回到标准库代码所在目录里查找文件,但你的头文件不在标准库目录,因此用引号, 在本地查找,否则用尖括号在代码库目录找不到.
C代码库目录一般在 /usr/include/ 目录下,如下
➜ CLang vim /usr/include/str
strhash.h stringlist.h struct.h
string.h strings.h
变量的作用域仅限于本文件中, 如果你想共用变量, 就该在头文件中声明,并在变量名前加上 extern 关键字, 比如
func.h 中 extern int passcode;
上面的sumTotal()只能自己使用,无法共享, 如果多个程序间要共享某个文件的代码该咋办???
只需要把sumTotal()代码放到一个独立文件中, 比如common.c
完整步骤:
- 新建一个头文件func.h
- 把sumTotal()方法代码放到common.c 中去
- 主程序要共享的话必须引入 func.h 头文件后才能使用共享代码里的方法
- 然后执行编译 gcc message_hider.c common.c -o message_hider
代码块如下:
func.h 头文件内容:
float sumTotal(float i);
common.c 内容:
float sumTotal(float i) {
return i * 2;
}
message_hider.c 内容:
#include <stdio.h>
#include "func.h" //引入自定义路径下的头文件
int main(){
printf("i=%f\n",sumTotal(5.5) );
}
执行编译 gcc message_hider.c common.c -o message_hider
vs 16. 编译的整个过程
- 预处理, 编译器用 #include预处理指令加载对应的头文件.
- 编译: 把C语言转成汇编代码
- 汇编: 生成二进制代码(或目标中间码)
- 链接: 把全部的目标代码连接在一起,生成一个可执行文件.
假如我的可执行文件由十几个源文件编译而成, 我只修改了其中一个文件, 全部重新编译的话就是资源浪费了, 那该咋办???
--> 提高编译速度, 不要重新编译所有文件
编译器编译时会为所有文件生成目标代码, 最后把目标代码连接起来变成可执行文件, 假如能只重新生成被修改后的源文件的目标代码, 那就不用编译所有文件了.
C源代码 --> 编译器 --> 目标代码 --> 连接器 ---> 可执行文件
比如上面的 gcc launch.c sum.c -o launch
文件关系如下
rate.h 有rateRate()和该方法声明
sum.c 里的subTotal()方法调用了rateRate()方法
subTotal()的声明在sum.h文件里
launch.c 里调用了 subTotal()方法来计算结果
因此
sum.c, rate.h --> sum.o |
|--> ./launch 可执行文件
launch.ch, sum.h --> launch.o |
实际上是下面两步合成的:
@1. 把源代码编译成目标文件
gcc -c *.c //匹配当前目录下的所有C源文件,-c 告诉编译器你想为所有源文件创建目标文件.
@2. 然后把所有目标文件链接起来
gcc *.o -o launch //陪陪当前目录下的所有目标文件链接问一个launch可执行文件
现在加入你只修改了 sum.c, 只需
gcc -c sum.c //不用编译launch.c了
gcc *.o -o launch
虽然链接还是重新链接所有,但在编译阶段省去了很多的时间.
只要发现源文件, 比目标文件的时间还新, 那么这个源文件就需要重新编译
但你必须得记住你修改了哪些文件, 尤其当几十几百个文件的时候.
比如上面的两对依赖关系如下:
sum.c, rate.h --> sum.o
launch.ch, sum.h --> launch.o
使用make : 一个帮你运行编译命令的工具,make会检查源文件和目标文件的时间戳,如果目标文件过期, 就会重新编译.
make可以帮你通过时间戳判断文件是否过期, 但make 还要知道文件间的依赖关系,并且你要告诉它对这种过期的依赖关系使用哪些指令, 才能帮你编译
因此make要知道的两个事情:
@1. 依赖项: 生成目标需要哪些文件.
@2. 生成方法: 生成该文件要使用哪些命令
这就需要使用makefiles 文件了, 把make索要知道的两个事情都列出来,如下:
launch.o: launch.c sum.h
gcc -c launch.c
#生成源文件sum.o的依赖关系
sum.o: sum.c rate.h
gcc -c sum.c #生成源文件的执行命令
#最后去链接目标文件
launch: launch.o sum.o
gcc launch.o sum.o -o launch #执行的命令(生成可执行文件)
然后在命令窗口下执行
[root@07 Cproject]# make launch
gcc -c launch.c
gcc -c sum.c
gcc launch.o sum.o -o launch
然后只修改sum.c 再执行 make launch
[root@07 Cproject]# make launch
gcc -c sum.c //只编译了sum.c
gcc launch.o sum.o -o launch
上面为啥make命令要指明 launch 可执行的文件名???
-> 因为你makefile里可能定义了多个可执行文件的生成, 不止一个launch
那难道不能直接像安装php一样就输入了make么?
./configure ...
make
make install
那肯定可以了, 只不过当你只使用make的时候, 你必须在makefile里定义一个all命令, 而且是放置在所有命令前面, 如下:
all: launch //默认只输入make的时候执行了make all, 对应的指令组是launch 即系统自己执行 make launch
你再次执行的 make install 也不过是makefile 里定义的一个install命令组, 下面的内容多半是替换/复制编译好的可执行文件到执行目录去. [有些只用一个make就安装成功的, 是因为在make all的其中一个指令中包含了类似的make install动作了]
输入 make launch 的原因是为了执行makefile里对应的launch对应的"gcc launch.o sum.o -o launch" 命令, 而这个又依赖于上面的2个目标文件sum.o, launch.o, 于是一步步推演, 最后到执行成功.
类似的你也可以用make执行编译
[root@07 Cproject]# make sum.o
gcc -c sum.c
再比如在上面的makefile文件后面追加入下面的代码,用于列出当前目录:
//...
sayhello:
pwd
[root@07 Cproject]# make sayhello
pwd
/media/psf/Home/share/Cproject
因此makefile 的作用越看越像是编辑 ~/.bash_profile , 上面的sum.o, sayhello , launch, ... 就像是alias 的命令名, 而下面的就是这个别名命令对应的执行语句
因此makefile 的语法
all: 多个命令名(放为第一个命令)[可选]
命令名: [可选依赖关系,空格隔开]
执行的命令1[可多条]
执行的命令2
比如曾经麦兜的Makefile代码片段如下:
install:
cp ${SRV_BIN_DIR}/router ${SRV_DIR}/router/bin/
cp ${SRV_BIN_DIR}/game ${SRV_DIR}/game/bin/
cp ${SRV_BIN_DIR}/broadcast ${SRV_DIR}/broadcast/bin/
bootstrap:
mkdir -p /data/home/user_00/server
mkdir -p /data/home/user_00/opsconf/qzone
cd /data/home/user_00/server && mkdir -p game router broadcast
cd /data/home/user_00/server/game && mkdir -p bin conf log
cd /data/home/user_00/server/router && mkdir -p bin conf log
cd /data/home/user_00/server/broadcast && mkdir -p bin conf lo
ant 是 java 下类似make的勾践工具
rake 是 ruby 下类似make的构建工具
生成makefile相当痛苦,要手动编辑, 你可以用autoconf工具来生成 makefile/configure 的文件内容.
[比如你之前在安装memcached扩展时进入该源码包目录发现没有configure文件, 执行 phpize 报找不到 autoconf , 一般情况下phpize执行完检查后会调用autoconf 生成configure文件, 这里找不到那么你就要yum install autoconf 后再执行phpize, 然后就会生成该扩展的configure 文件了 ]
vs 17. 结构体
const char * 表示将传递字面值.
struct fish {
const char *name;
const char *species;
int teeth;
int age;
}
实例化:
struct fish a = {"snappy", "prianha", 69, 4}
@2.结构定义简写:
实例化的时候还要把结构类型(struct fish)带上, 那是相当麻烦.
可以用typedef 来为结构体命名(起别名)
typeofdef struct fish {
//...
} fishes;
现在把struct fish 起别名(类型名)为fishes了, 所以实例化时可以直接:
fishes f = {"snappy", "prianha", 69, 4}
或者直接连结构体名都不要,只保留类型名(别名), 如下
typedef struct {
//...
} fishes;
[唯一例外 :在链表那种递归结构中不能省略结构名]
实例化结构变量过程简化为:
fishes f = {"snappy", "prianha", 69, 4};
把一个结构变量赋值给另一个结构变量, 计算机会赋值结构的内容(副本), 如果结构体中有指针,那么赋值的仅仅是指针的地址值.
struct fish b = a;
那么 b 和 a 的name和species 是指向同一块地址的[因为他们是指针,不会复制副本], 而teeth, age是相互独立的一块[地址和存储].
因此函数传参的时候要想改变结构体字段值,形参必须传指针, 否则是拷贝副本的
//不会改变原结构, 因为形参f是源f的一个副本
happy_fish(f);
void happy_fish(fish f) {
f.age = 9;
}
//方法体会改变结构, 因为传的是源f的指针
happy_fish(&f);
void happy_fish(fish *f) {
(*f).age = 9;
(*f).care.exercise.duration = 9.9; //多层嵌套的访问
}
上面 (*f).age 中括号非常重要, 不加会出错的, 不加表示
把*放变量名前并括号,表想得到指针指向的值.
而*t.age 中t表示地址,地址没有age的内容, 直接就报错了.
仅在指着下可使用"->"表示结构体的访问:
但这种(*t).age的方式还是太难写了, 可该用"->"
因此(*f).age 和 t->age 含义相同.
因此上面的可改写成:
void happy_fish(fish *f) {
t->age = 9; //由t指向的结构中age的字段
t->care.exercise.duration = 9.9; //由t指向的结构中care.exercise.duration的字段
}
```
![image](https://note.youdao.com/yws/public/resource/340d5e702b482d79e98d703e8c6ae291/xmlnote/523696B4EDF34175ACBF9B8B05550861/9269)
```
@3. 结构嵌套
struct perference {
const char *food;
float exercise_hours;
}
typedef struct {
const char *name;
const char *species;
int teeth;
int age;
//嵌套了一个结构体, care表身在fish结构体中的字段名
struct perference care;
} fish;
fish f = {"snappy", "water", 64, 4, {"checken", 4.5}};
printf("teeth=%d,food=%s", f.teeth, f.care.food );
//输出 teeth=64,food=checken
@4. 结构字段按名访问, 用<jiegou >.<字段> de 点表示法.进行读取和更新
f.teeth = 90;
结构嵌套加简写:
#include <stdio.h>
struct exercise {
const char *description;
float duration;
};
struct meal {
const char *ingredients;
float weight;
};
struct perferences {
struct meal food;
struct exercise exercise;
};
typedef struct {
const char *name;
const char *species;
int teeth;
int age ;
struct perferences care;
} fish;
int main(){
fish f = {"fish", "water life", 64, 4, {{"meal", 2.2}, {"exercise",4.5 } } };
printf("teeth=%d, meal=%s, duration=%f", f.teeth, f.care.food.ingredients, f.care.exercise.duration);
return 0;
}
vs 18. union联合
我的结构体中有三种字段类型, 我可能不会全部都赋值使用, 我不想给三个字段都分配空间,这样太浪费了.
联合union结构: 使用时才根据赋值决定你保存的类型(计算机会使用其中最大的字段来分配空间以防止溢出)
typedef union {
short count;
float weight;
float volume;
} quantity;
初始化联合, 当有两个字段数据类型一样的时候,为了辨别你的目标必须用 ".字段名=val" 的方式来赋值.
@1. quantity c = {10}
@2. quantity q = {.weight=1.5, .count=2 }
@3.或者 :
quantity q;
q.count = 15;
联合和结构体配合使用
typedef union {
short count;
float weight;
float volume;
} quantity;
typedef struct {
const char *name;
const char *country;
quantity amount;
} fruit_order;
fruit_order apples = {"apples", "england", .amount.weight=4.2};
printf("weight=%f", apples.amount.weight); //输出 4.20000
看上面结构体重的联合赋值是 .amount.wight=4.2 最前面有个"."点号表这个字段是联合
vs19. 枚举
enum colors
{
RED,
GREEN,
PUCE
};
使用枚举:
enum colors favorite = PUCE; //这个值只能在枚举中的,否则报错
或者简写的方式:
typedef enum {
RED,GREEN,PUCE
} colors;
colors cs = PUCE;
vs 20.位字段
假如你有很多只有几个变化的数据, 比如choice字段的值不是1就是0, 如果我定义一个int或short还是浪费的, 我可不可以之用一个二进制位???
typedef struct {
unsigned int first_visit:1;
unsigned int come_again:1;
unsigned int fingers_lost:4;
unsigned int shark_attack:1;
unsigned int days_a_week:3;
//...
} synth;
每个字段必须是 unsigned int, 后面的:1表示使用1个位空间,即只有0,1, 而:3 表示0-7的值共8种
synth sy = {1,0,15,0,7};
嗨翻C语言笔记(一)的更多相关文章
- 嗨翻C语言笔记(二)
~a a中所有位都取反 a & b a中的位 与 b中的位 (都为1则1,否则为0) a | b a中的位 或 b中的位 (只要对应位一个位1则为1) a ^ b a中的位 亦或 b中的位 & ...
- 嗨翻C语言
<嗨翻C语言> 基本信息 作者: (美)David Griffiths Dawn Griffiths 译者: 程亦超 出版社:人民邮电出版社 ISBN:978711531884 ...
- 嗨翻C语言--这里没有蠢问题(一)
问:card_name[0]是什么意思?答:它是用户输入的第一个字符.如果用户输入了10,那么card_name[0]就将是1.问:总是得用/*和*/写注释吗?答:如果你的编译器支持C99标准,就可以 ...
- 第一篇代码 嗨翻C语言 21点扑克
/* * 计算牌面点数的程序. * 使用“拉斯难加斯公开许可证”. * 学院21点扑克游戏小组. */#include <stdio.h>#include <stdlib.h& ...
- C语言学习书籍推荐《嗨翻C语言(英文)Head First C》下载
David Griffiths (作者), Dawn Griffiths (作者) Ever wished you could learn C from a book? Head First C pr ...
- R语言笔记
R语言笔记 学习R语言对我来说有好几个地方需要注意的,我觉得这样的经验也适用于学习其他的新的语言. 语言的目标 我理解语言的目标就是这个语言是用来做什么的,为什么样的任务服务的,也就是设计这个语言的动 ...
- R语言笔记4--可视化
接R语言笔记3--实例1 R语言中的可视化函数分为两大类,探索性可视化(陌生数据集,不了解,需要探索里面的信息:偏重于快速,方便的工具)和解释性可视化(完全了解数据集,里面的故事需要讲解别人:偏重全面 ...
- Scala语言笔记 - 第一篇
目录 Scala语言笔记 - 第一篇 1 基本类型和循环的使用 2 String相关 3 模式匹配相关 4 class相关 5 函数调用相关 Scala语言笔记 - 第一篇 最近研究了下scala ...
- Go 语言笔记
Go 语言笔记 基本概念 综述 Go 语言将静态语言的安全性和高效性与动态语言的易开发性进行有机结合,达到完美平衡. 设计者通过 goroutine 这种轻量级线程的概念来实现这个目标,然后通过 ch ...
随机推荐
- iview中关于table组件内放入Input会失去焦点
table里面的数据是一个数组,父组件传入的.子组件是截图的内容.当每个input框数据发生变化时,把数据传给父组件.在父组件做表单的提交. github内已经提到过这个问题(https://gith ...
- Spring Boot 整合 Thymeleaf 完整 Web 案例
Thymeleaf 是一种模板语言.那模板语言或模板引擎是什么?常见的模板语言都包含以下几个概念:数据(Data).模板(Template).模板引擎(Template Engine)和结果文档(Re ...
- SQL Server ->> 间接实现COUNT(DISTINCT XXX) OVER(PARTITION BY YYY)
SQL Server 2005版本开始支持了窗口函数(Windowing Function)和OVER字句.SQL Server 2012版本开始支持了窗口函数的ORDER BY字句实现连续/累计聚合 ...
- SQL Server ->> 数据一致性检查命令 -- DBCC CHECKDB
Comming soon!!! 参考文献: CHECKDB From Every Angle: Complete description of all CHECKDB stages
- UnicodeDecodeError: 'utf8' codec can't decode byte in position invalid start byte
在scrapy项目中,由于编码问题,下载的网页中中文都是utf-8编码,在Pipeline.py中方法process_item将结果保存到数据库中时,提示UnicodeDecodeError: 'ut ...
- C#中的多线程 - 同步基础 z
原文:http://www.albahari.com/threading/part2.aspx 专题:C#中的多线程 1同步概要Permalink 在第 1 部分:基础知识中,我们描述了如何在线程上启 ...
- 20140322 卡迪夫城VS利物浦,拔出重剑,有惊无险
一.菱形442 起初在客战南安普顿的时候,罗杰斯启用了菱形442阵式,阵容和今天客战卡迪夫城几乎一样,只是格伦·约翰逊打左后卫,弗拉纳甘任职右后卫,目的是为了在客场抵御卢克·肖+拉拉纳.当时库蒂尼奥的 ...
- jq仿 妙味课堂导航01
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8&quo ...
- sqlite3如何退出...>状态
一般是进入SQL数据语言模式了,此时要想退出...>状态,只要输入一条完整的SQL语句就行了,也就是末尾要加上:(分号)这个符号就可以退回到sqlite>状态
- 交叉熵Cross-Entropy
1.交叉熵:用来描述通信中将一个概率分布的最优编码用到另一个概率分布的平均比特数 公式: 2.交叉熵是不对称的 3.交叉熵的作用是表达两个概率分布的差异性 设概率分布p(x)和q(x),两个概率分布差 ...