文章目录
- 1.数据类型:编译器(compiler)与解释器(interpreter),中文里的汉字和标点符号是两个字节,不能算一个字符(单引号)
- 2.运算符/循环:sizeof/size_t
- 3.数组:存数据类型相同的数据,数组下标越界导致段错误
- 4.指针:指针数组:这个数组的所有元素都是指针类型。数组指针:这个指针存放着一个数组的首地址
- 4.1 地址/值/变量名:p是地址,*p是指向的内容,01指0x01,&a:拿变量a的地址赋给
- 4.2 函数调用:复制a/指向a
- 4.3 函数返回:复制指针
- 4.4 数组和指针转换:指针转不了数组,数组可转为指针,&取地址
- 4.5 字符串str相关:putchar('w');putchar('w');
- 5.结构体:存放一组不同数据类型的数据
- 6.main函数的参数:main函数的参数是从命令提示符下执行程序时传入
- 7.static/inline/回调/extern/堆内存:inline内联函数本身就是static(本文件私有)函数,inline函数在c中作用性不高,c中用习惯了宏定义
- 8.编译预处理:C源程序 - 编译预处理【#开头指令和特殊符号进行处理,删除程序中注释和多余空白行】- 编译
- 9.gdb调试:多进/线程中无法用
- 10.makefile文件:make是一个解释makefile中指令的命令工具
1.数据类型:编译器(compiler)与解释器(interpreter),中文里的汉字和标点符号是两个字节,不能算一个字符(单引号)
编译型语言
:C/C++/golang/rust:并且都是AOT(ahead of time)预先编译,编译将源码编译成机器码生成二进制文件,可直接运行该文件,因为是机器码,所以运行效率很高。缺点
:不够灵活,改代码要重新编译,此外平台依赖,linux平台编译出来的二进制文件无法在windows运行,跨平台还需借助交叉编译。
解释型语言
:python/js/php:不需要生成二进制文件,灵活如线上php系统,改了代码功能直接生效,但运行效率低。
半解释半编译型语言
:java:JIT将运行到的代码块在运行时编译成机器码,既可保证跨平台性,又能使热代码高效运行。
C语言库函数声明头文件(.h)
存放在/usr/include目录中,库函数定义(具体实现)文件(.c)
是/usr/lib/gcc/x86_64…/4.4.4/libgcc.a(打包好的静态库),gcc编译不用包含libgcc.a(缺省会包含)。
gcc main.c int_sum.c float_sum.c -o main -Wall(显示所有警告) -I../include(-I后面没空格,不加-I就在main.c中指定头文件的相对路径)
gcc main.c -o main -Wall -I../include -L../lib -lsum(有库文件/lib/libsum.so就只要main.c就行)
2个字节的-480
在计算机中为0xfe20,ipmitool发指令:低位在前即0x20 0xfe。
先确定字节数,再将最高位1固定。
浮点数不是2的0次方+2的1次方…,用下面公式(不用记住)。
浮点数最小可识别精度和浮点数即0.1本身大小相关,而不是和float相关。
计算机中以字节为单位存储数据,1字节Byte=8bit。
1.整数型,字符型,浮点型变量初始化
:就是赋0值,也可在定义时初始化。
int ii=0; // 定义整数型变量并初始化
char cc=0; // 定义字符型变量并初始化
double money=0; // 定义浮点型变量并初始化
也可以先定义,然后再初始化:
int ii; // 定义整数型变量
char cc; // 定义字符型变量
double money; // 定义浮点型变量
ii=0; // 初始化ii为0
cc=0; // 初始化cc为0
money=0; // 初始化money为0
2.字符串变量的初始化
:对字符串变量来说,初始化就是把内容清空。
char name[21]; // 定义一个可以存放20字符的字符串
memset(name,0,sizeof(name)); // 清空字符串name中的内容
关键字
:共32个,也就是说这些单词在C语言中有特别的含义,程序员不能把它用于变量或函数的命名。auto
:声明自动变量。break
:跳出当前循环。case
:开关语句分支。char
:声明字符型变量或函数返回值类型。const
:声明只读变量。continue
:结束当前循环,开始下一轮循环。default
:开关语句中的“默认”分支。do
:循环语句的循环体。double
:声明双精度浮点型变量或函数返回值类型。else
:条件语句否定分支(与 if 连用)。enum
:声明枚举类型。extern
:声明变量或函数是在其它文件或本文件的其他位置定义。float
:声明浮点型变量或函数返回值类型。for
:一种循环语句。goto
:无条件跳转语句。if
:条件语句。int
: 声明整型变量或函数。long
:声明长整型变量或函数返回值类型。register
:声明寄存器变量。return
:子程序返回语句(可以带参数,也可不带参数)。short
:声明短整型变量或函数。signed
:声明有符号类型变量或函数。sizeof
:计算数据类型或变量长度(即所占字节数)。static
:声明静态变量。struct
:声明结构体类型。switch
:用于开关语句。typedef
:用以给数据类型取别名。unsigned
:声明无符号类型变量或函数。union
:声明共用体类型。void
:声明函数无返回值或无参数,声明无类型指针。volatile
:说明变量在程序执行中可被隐含地改变。while
:循环语句的循环条件。
1.数据输出:
在C语言中,有三个函数可以把数据输出到屏幕。
putchar 用于输出单个字符。
puts 输出字符串。
printf函数是格式化输出函数, 用于向屏幕输出数据,printf函数的调用方法是: printf(格式化字符串,参数列表);
2.输出整数
输出的整数常量或整数变量用%d表示,在参数中列出待输出的整数常量或整数变量。
printf(“我年龄是%d岁。\n”,18);
int age=18;
printf(“我年龄是%d岁。\n”,age);
3.输出字符
输出的字符常量或字符变量用%c表示,在参数中列出待输出的字符常量或字符变量。
printf(“我姓别是:%c。\n”,‘x’); // 姓别:x-男;y-女
char xb=‘x’;
printf(“我姓别是:%c。\n”,xb);
4.输出浮点数
输出的浮点型常量或浮点型变量用%lf表示,在参数中列出待输出的浮点型常量或浮点型变量。
printf(“我体重是%lf公斤。\n”,62.5);
double weight=62.5;
printf(“我体重是%lf公斤。\n”, weight);
5.输出字符串
输出的字符串常量或字符串型变量用%s表示,在参数中列出待输出的字符串常量或字符串变量。
char name[21];
memset(name,0,sizeof(name));
strcpy(name, “豫让”);
printf(“我的姓名是%s。\n”,name);
d:十进制。o:八进制。x:十六进制。
2.运算符/循环:sizeof/size_t
||
左边的命令返回假(命令返回值 $? == 1),||
右边的命令才会被执行,和c语言逻辑或
相同。
如下ii++就是ii=ii+1。前先加1。
=是赋值,==才是判断两个数是否相等,C语言没有(之间)的关系运算符,如年龄在25-30岁之间:年龄大于等于25岁并且年龄小于等于30岁。
sizeof运算符(不是函数)
计算某一个变量在当前系统的内存中所需占用的字节数:
1.用于数据类型
:sizeof(int)) = 4,sizeof(指针)大小永远是8字节。
2.用于变量
:sizeof使用形式:sizeof(var_name)或sizeof var_name。
sizeof结果类型是size_t:typedef unsigned int size_t
为无符号整型,长度为4个字节(32位系统)。typedef unsigned long size_t
为无符号长整型,长度为8个字节(64位系统)。ssize_t是有符号整型
(在32位机器上等同int,在64位机器上等同long int)。
不要在if(判断条件)后面加分号。有;号就为空语句,下面都执行。c=(a>b)?a:b等同于两行:if(a>b) c=a;else c=b; if(0)
即0假。if(a=b)是赋值,不是判断。当没有default时,如果所有case都匹配失败,则什么都不执行。char day=0,scanf(‘%c’',&day),case ‘0’。
#include<stdio.h>
int main()
{
int a=1, b=2, re;
char c;
scanf("%c", &c);
switch(c)
{
case '+':
re = a + b;
break;
case '#':
{
int other = 3; // switch语句里定义了新的变量 ,加{}
re = a + b + other;
break;
}
default:
printf("Illegal input!\n");
break;
}
printf("%d\n", re);
}
$gcc -o main *.c -lm
$main
Illegal input!
0
while(真)执行,continue跳到循环首部,break跳出循环。
如下do-while先执行一次循环。
3.数组:存数据类型相同的数据,数组下标越界导致段错误
如下如果用msg[1],虽然越界了,但是系统给结构体分配很多字节,所以不报错。可用msg[0]到msg[n-1]。
4.指针:指针数组:这个数组的所有元素都是指针类型。数组指针:这个指针存放着一个数组的首地址
4.1 地址/值/变量名:p是地址,*p是指向的内容,01指0x01,&a:拿变量a的地址赋给
#include<stdio.h>
char str[10];
char strbuf[10];
char *a(void)
{
FILE *fp=0;
if ((fp=fopen("a","rt")) ==0) //a文件里写了 333aaa
{
printf("111");
}
if (fgets(strbuf, 10, fp) == NULL)
{
printf("222");
fclose(fp);
}
strcpy(str,"B");
strcat(str,strbuf);
fclose(fp);
return str;
}
int main()
{
char *b=a();
printf("%s\n", b);
}
int *p 未赋值的指针称为野指针(危险),a=100,p=&a指向合法区域。或如下空指针的两种写法也可防止野指针。
4.2 函数调用:复制a/指向a
1.
如下main中走到increament跳到这函数中,拷贝一份给increament中的a。main中的a和increament中的a各自独立一块内存,只是名字一样。
2.
一定要通过increasement函数对a有修改怎么办?如下必须用到指针,increament运行结束后指针销毁。
4.3 函数返回:复制指针
如下当调用move_p时,p要往右移一位:move_p参数定义虽是指针,但调用时传入也是指针,复制一份。
4.4 数组和指针转换:指针转不了数组,数组可转为指针,&取地址
4.5 字符串str相关:putchar(‘w’);putchar(‘w’);
#include <stdio.h>
#include <string.h>
int main()
{
char *names = "PSU";
if(strcmp(names,"PSU")==0)
{
puts("aaaa"); // 打印出
}
}
int main() { // error: expected ‘)’ before string constant 少了int main()
char name_str[30];
char name_str1[30];
strcpy(name_str,"/sys/bus/i2c/devices/");
strcpy(name_str1,"17-0064");
strcat(name_str,name_str1);
printf("is : %s\n", name_str); // /sys/bus/i2c/devices/17-0064
// printf("[%s]__%4d__[%s] %s \n", __FILE__, __LINE__, __FUNCTION__, n->name);
}
#include <stdio.h>
#include <string.h>
int main ()
{
char str1[15];
char str2[15];
int ret;
strcpy(str1, "abcdef");
strcpy(str2, "ABCDEF");
ret = strcmp(str1, str2);
if(ret < 0)
{
printf("str1 小于 str2");
}
else if(ret > 0)
{
printf("str1 大于 str2");
}
else
{
printf("str1 等于 str2");
}
return(0); //str1 大于 str2
}
#include <stdio.h>
#include <stdlib.h>
#include<string.h>
#include <pthread.h>
#include <unistd.h>
#if 0
int main() {
// int a;
// char pNum[]="0x7f";
// a=strtoul(pNum,0,16);
// printf("%d\n",a); //127 ,%x也是127
// return 0;
char str[30] = "2030300 This is test";
char *ptr;
long ret;
ret = strtoul(str, &ptr, 10);
printf("数字(无符号长整数)是 %lu\n", ret); // 2030300
printf("字符串部分是 |%s|\n", ptr); // This is test
return 0;
}
#endif
char temp_log_0[100] = {0};
sprintf(temp_log_0, " '%d' ", a);
char temp_log_1[100] = "echo ";
strcat(temp_log_1, temp_log_0);
char temp_log_2[100] = " >> /var/log/a.log";
strcat(temp_log_1, temp_log_2);
system(temp_log_1); // C
if(-1 == std::system(temp_log_1)){}; // CPP
if(-1 == std::system("echo 'aaa' >> /var/log/a.log")){};
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
void cp (char *path_from, char *path_to)
{
FILE *fp_read = NULL;
FILE *fp_write = NULL;
char ch = !EOF;
fp_read = fopen(path_from, "r"); ///
// if (fp_read == NULL)
// {
// printf("您没有这个(%s)文件\n", path_from);
// }
fp_write = fopen(path_to, "w");
while ((ch = fgetc(fp_read)) != EOF) // 读
{
fputc(ch,fp_write); // 写
}
fclose(fp_read);
fclose(fp_write);
}
int main(void)
{
char path_from[50];
char path_to[50];
printf("输入文件名:"); // D:\1.txt
scanf("%s", path_from);
printf("\n输入文件名:"); // D:\2.txt,不存在会自动创建
scanf("%s", path_to);
cp(path_from,path_to);
return 0;
}
#include<stdio.h>
#include<assert.h>
char* my_strcpy(char* dest, char* src) //所以我这可以用两个char*类型了指针来接收
{
assert(dest && src);//这个的意思就是避免dest和src是空指针,(如果有了这个assert(断言),就可以使当它们其中之一有空指针的时候就会报错,避免程序运行不报错,但要注意引头文件)
char* ret = dest;//写这步的好处和原因有两个 1.可以使我的dest发生改变的时候还有一个指针指向它,使我便与寻找 2.可以使我的返回类型变得更加完美,完美实现char*的返回值的目的
while (*src)
{
*dest = *src;//这个的意思就是把源头的字符赋值给目的地
dest++;//这两步一样就是使指针指向下一个字符,然后再循环
src++;
}
*dest = *src;//这步的目的就是因为上面那个循环的条件是 src!='\0',所以当src为'\0'时,循环就会停止,导致*dest = *src这步在最后不能实现,所以'\0'就没有拷贝到dest中,所以我最后还要再进行一步赋值
// while (*dest++ = *src++); // 这步的还是解引用后直接复制的意思,只是放在了循环之中(意思为当src为'\0'时,dest也为'\0',并且'\0'的ASCII码值为0,所以为假,所以此时循环依然停止), 这行可替代上面7行
return ret;
}
char* my_strncpy(char* dest, char* src, size_t num) //比上面多了一个n
{
assert(dest && src);//同理
char* ret = dest;
while (num)
{
*dest = *src; //复制
src++; //源地址往后+1
dest++;
num--; //我所需要拷贝的字符数随着我的循环一直减减
}
return ret;
}
int main()
{
char arr1[] = "abcdef";
char arr2[20] = "bcd";
my_strcpy(arr2, arr1);//因为数组就是首元素的地址,所以这边传上去的其实就是两个地址
printf("%s",arr2);
return 0;
}
5.结构体:存放一组不同数据类型的数据
如下最后一行*pst
就是queen结构体变量。
结构体复制:基本类型用=,字符串用strcpy,结构体memcpy。
结构体作为函数的参数:结构体成员较多,函数参数的初始化和赋值的开销很大,最好的办法就是传递结构体变量的地址。
6.main函数的参数:main函数的参数是从命令提示符下执行程序时传入
#include <stdio.h>
int main(int argc, char **argv) {
char *stty, *dev;
dev = argv[1];
stty = argv[2];
printf("%s\n%s\n",dev,stty);
}
// a.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
extern int h_errno;
int main(int argc, char **argv)
{
char *ptr, **pptr;
char str[INET_ADDRSTRLEN];
struct hostent *hptr; //
while (--argc> 0)
{
ptr = *++argv; //传入的域名
if ( (hptr = gethostbyname (ptr) ) == NULL) //完成域名解析
{
printf("gethostbyname error for host: %s: %s",ptr, hstrerror (h_errno) );
continue;
}
printf ("official hostname: %s\n", hptr->h_name);
for (pptr=hptr->h_aliases; *pptr!= NULL; pptr++)
printf ("\talias: %s\n", *pptr);
switch (hptr->h_addrtype)
{
case AF_INET:
pptr = hptr->h_addr_list;
for ( ; *pptr != NULL; pptr++)
printf ("\taddress: %s\n",inet_ntop (hptr->h_addrtype, *pptr, str, sizeof (str))); //hptr->h_addrtype我们获取的IP地址
break;
default:
printf("unknown address type");
break;
}
}
exit(0);
}
$ gcc a.c -o a
$ ./a www.baidu.com
official hostname: www.a.shifen.com
alias: www.baidu.com
address: 180.101.50.188 (浏览器输入都会跳转到百度)
address: 180.101.50.242
7.static/inline/回调/extern/堆内存:inline内联函数本身就是static(本文件私有)函数,inline函数在c中作用性不高,c中用习惯了宏定义
什么函数被定义成内联函数呢?1.
经常被调用(如果不经常被调用,节省一点时间也没意义),2.
函数体里代码少(如果函数体里代码多,执行时间远大于跳转时间)。
如下左边的你有一个私人厨师,你将50块钱和厨师电话作为参数传给老好人函数,老好人做了一些买菜挑菜等杂活后调用你的厨师进行做饭,老好人不仅为你服务还为其他人服务。
如下方框是一个c文件,右边的c文件可以调到左边的var变量,每一个c文件不管里面写什么都可以编成o文件,右边的o文件var地址留空。最后链接成二进制文件时,链接就是编译,所以和1(外链)和2(内链)相关。
extern表示引用外部的变量,从外面来的。
8.编译预处理:C源程序 - 编译预处理【#开头指令和特殊符号进行处理,删除程序中注释和多余空白行】- 编译
条件编译:最常用的两种格式#ifdef和#ifndef 。#undef :取消已定义的标识符
如下book145.c和_public.c都有 #include"_public.h",会重复包含。
在_public.c中如下这样写,_public.h就不会被重复包含。
9.gdb调试:多进/线程中无法用
root 用户:yum -y install gdb,gdb -v。
10.makefile文件:make是一个解释makefile中指令的命令工具
vi gcc.sh 如下,sh gcc.sh。gcc -o 目标 依赖1 依赖2。makefile命令能被执行条件有两个:1.目标不存在,2.依赖已更新。
如上若只需要编译book2,单个文件改变不重复编译其他文件即增量编译。vi makefile,$前一个tab键不能8个空格。make默认是make all
,如果将all这行book3删除,则make不会编译book3,可指定make book3,book3相当于标签。-欧2是让编译效率最高,一般正式发布用。gcc命令选项 :-c编译不链接。