C语言 - 基础、理论(原码/反码/补码)、函数(字符串、定义)
概念
原码、反码、补码
原码
一个数的原码(原始的二进制码)有如下特点:
- 最高位作为符号位,0表示正,1表示负
- 其他数值部分就是数值本身绝对值的二进制数
- 负数的原码是在其绝对值的基础上,最高位变为1
下面数值以1字节的大小描述
| 十进制 | 原码 |
|---|---|
| +15 | 0000 1111 |
| -15 | 1000 1111 |
| +0 | 0000 0000 |
| -0 | 1000 000 |
原码表示简单易懂,与带符号数本身转换方便,只要符号还原即可,但当两个正数相减或不同符号数相加时,必须比较两个数哪个绝对值大,才能决定谁减谁,才能决定结果是正还是负,所以 原码不便于加减运算 。
反码
- 对于正数,反码与原码相同
- 对于负数,符号位不变,其他部分取反(1变0,0变1)
| 十进制 | 反码 |
|---|---|
| +15 | 0000 111 |
| -15 | 1111 0000 |
| +0 | 0000 0000 |
| -0 | 1111 1111 |
反码运算也不方便, 通常用来作为求补码的中间过度 。
补码
在计算机系统中,数值一律用补码来存储。
补码特点:
- 对于正数,原码、反码、补码相同
- 对于负数,其补码为它的反码加1
- 补码符号位不动,其他位求反,最后整个数加1,得到原码
| 十进制 | 反码 |
|---|---|
| +15 | 0000 111 |
| -15 | 1111 0001 |
| +0 | 0000 0000 |
| -0 | 0000 0000 |
例子 
补码的意义
- 取反
不管以原码方式存储,还是以反码方式存储,0也有两种表示形式。(用于区分符号位)
但是如果以补码方式存储, 补码统一了0的编码 - 反码加一 (也可以理解位原码减一)
左移动
关键字

数据类型

几种基本数据类型的字符(Byte)大小(32位):
short、int、long、long long
常量表示
| 整型常量 | 所需类型 |
|---|---|
| 10 | int |
| 10l, 10L | long |
| 10u, 10U | unsigned int |
| 10ul, 10UL | unsigned long |
| 10ull, 10ULL | unsigned long long |
打印格式(进制维度)
| 打印格式 | 含义 |
|---|---|
| %的 | 有符号 十进制 |
| %u | 十进制 无符号 |
| %o(字母o) | 八进制 |
| %x | 十六进制 小写小写字母 |
| %X | 十六进制 大写字母 |
打印格式(类型维度)
| 打印格式 | 含义 |
|---|---|
| %hd | short |
| %d | int |
| %l | long |
| %ll | long long |
| %hu | unsigned short |
| %u | unsigned int |
| %lu | unsigned long |
| %llu | unsigned long long |
# include <stdio.h> int main ( void ) { int a ; scanf ( "%d" , & a ) ; // 取地址 printf ( "%d\n" , a ) ; return 0 ; }
char
字符型变量用于存储一个单一字符,在C语言中用char表示,其中每个字符变量都会占用1个字节,在给字符型变量赋值时,需要用一堆英文半角格式的单引号( '' )把字符括起来。
字符变量实际上并不是把该字符本身放到变量的内存单元中去,而是将该字符对应的ASCII编码放到变量的存储单元中, char的本质就是一个1字节大小的整型
# include <stdio.h> int main ( void ) { // char a = 'A'; char a ; a = getchar ( ) ; // printf("%c\n", a); putchar ( a ) ; putchar ( '\n' ) ; return 0 ; }
float、double
浮点型变量是用来存储小数值的。在C语言中,浮点型变量分为两种:单精度浮点数(float)、高中生出国留学双精度浮点数(double)。 double型变量所表示的浮点数比float型变量更精确 (double型变量表示范围比float型变量大)
( 浮点型变量语法上可以运算,但因为精度问题,不适合用于运算 )
# include <stdio.h> int main ( void ) { float r = 2 ; float s = r * r * 3.14 ; printf ( "%.2f\n" , s ) ; // 保留两位小数 // 科学计数法 float a = 3.2e3f ; // 3.2*1000 = 32000, e可写为E float b = 1000e-3f ; // 0.1 return 0 ; }
数组
整型数组
# include <stdio.h> int main ( void ) { int arr [ 10 ] = { 1 , 4 , 2 , 3 , 5 , 6 , 9 , 8 , 7 , 0 } ; int len = sizeof ( arr ) / sizeof ( 0 ) ; printf ( "%d\n" , len ) ; // 冒泡排序 for ( int i = 0 ; i < len ; i ++ ) { for ( int j = i ; j < len ; j ++ ) { int a = arr [ i ] ; int b = arr [ j ] ; if ( a > b ) { int t = a ; arr [ i ] = b ; arr [ j ] = t ; } } } // 展示 for ( int i = 0 ; i < len ; i ++ ) { printf ( "%d " , arr [ i ] ) ; } printf ( "\n" ) ; return 0 ; }
二维数组
# include <stdio.h> int main ( void ) { int t [ 3 ] [ 5 ] = { { 1 } , { 2 , 3 } , { 0 } } ; int len = sizeof ( t ) / sizeof ( t [ 0 ] ) ; for ( int i = 0 ; i < len ; i ++ ) { int len2 = sizeof ( t [ 0 ] ) / sizeof ( t [ 0 ] [ 0 ] ) ; for ( int j = 0 ; j < len2 ; j ++ ) { printf ( "%d " , t [ i ] [ j ] ) ; } printf ( "\n" ) ; } printf ( "\n" ) ; return 0 ; }
字符数组
# include <stdio.h> int main ( void ) { char buff [ ] = { 'h' , 'e' , 'l' , 'l' , '\0' , '0' } ; printf ( "%s\n" , buff ) ; return 0 ; }

%s遇到\0就停止
类型限定符
| 限定符 | 含义 |
|---|---|
| extern 外部的,不在内部的 |
声明一个变量,extern声明的变量没有建立存储空间。 extern int a; |
| const | 定义一个常量,常量的值不能修改。 const int a = 10; |
| volatile | 防止编译器优化代码 (多线程时候,变量的可见性) |
| register | 定义寄存器变量,提高效率。register是建议型的指令,而不是命令型的指令。如果CPU有空闲寄存器,那么register就生效,如果没有空闲寄存器,那么register无效。 |
常量
常量的定义
const 数据类型 常量名- 【宏定义】
#define常量名 值![在这里插入图片描述]()
注意
- 通过
#define定义的常量是根据值来匹配数据类型的- const 修饰的常量是不安全的,可以通过指针来修改
一些C语言特殊的运算符
goto
# include <stdio.h> int main ( void ) { goto Here ; printf ( "hello world\n" ) ; Here ; printf ( "here" ) ; return 0 ; }
& (变量地址)
# include <stdio.h> int main ( void ) { int arr [ 10 ] ; printf ( "数组的位置: %p\n" , arr ) ; printf ( "数组0号位的位置: %p\n" , & arr [ 0 ] ) ; printf ( "数组的位置+1: %p\n" , arr + 1 ) ; printf ( "数组0号位的位置+1: %p\n" , & arr [ 0 ] + 1 ) ; return 0 ; }

函数
打印
进制的打印
int a = 10 ; // 十进制中的10 int b = 010 ; // 八进制中的10 int c = 0x10 ; // 十六进制中的10 int d = 0X10 ; // 十六进制中的10 /*
%d 将数据按照十进制输出
%o 将数据按照八进制输出
%x 将数据按照十六进制小写输出
%X 将数据按照十六进制大写输出
*/
字符串函数
gets() 和 puts()
int puts(const char *s)
标准设备输出字符串,在输出完成后自动输出一个 '\n'
# include <string.h> int main ( void ) { char ch [ 100 ] ; // 获取一个字符 gets ( ch ) ; // 输出一个字符串,并换行 puts ( ch ) ; return 0 ; }
char *gets(char *s)
从标准输入输入字符,并保存到制定的内存空间,知道出现换行符或督导文件结尾为止。
gets(str)与scanf("%s", str)的区别:
gets(str)允许输入额字符串包含有空格scanf("%s", str)不允许含有空格。需要写成scanf("%s[^\n]",str)注意:由于scanf和gets无法知道字符串s大小,必须遇到换行符或督导文件结尾才结束接收输入,因此容易导致字符数组越界(缓冲区溢出)的情况。
fgets() 和 fputs()
char *fgets(char *s, int size, FILE *stream);
从stream指定的文件内读入字符,保存到所指定的内存空间,直到出现换行字符、读到文件结尾或者已读了size-1个字符为止,最后会自动加上字符 '\0' 作为字符串结束。
参数:
s:字符串
size:指定最大读取字符串的长度(size-1)
stream:文件指针,如果读键盘输入的字符串,固定写为stdin
返回值:
成功:成功读取的字符串
督导文件尾或出错:NULL
# include <string.h> # include <stdio.h> // stdin int main ( void ) { char arr [ 10 ] ; // 参数:字符指针 大小 文件流(std in 标准输入流) fgets ( arr , 5 , stdin ) ; puts ( arr ) ; return 0 ; }
- 如果
输入的大小小于原始指针对应区域的大小,会在字符串输入完成时自动加上\n\0- 如果
输入的大小大于等于原始指针对应的区域大小,会自动加上\0, 而不会加上\n
int fputs(const char *str, FILE *stream);
将str所指定的字符串写入到stream指定的文件中,字符串结束符 \n 不写入文件。
参数
str:字符串
stream:文件指针,如果把字符串输出到屏幕,固定写为stdout
返回值
成功:0
失败:-1
# include <string.h> # include <stdio.h> // stdin int main ( void ) { char arr [ 10 ] ; // 参数:字符指针 大小 文件流(std in 标准输入流) fgets ( arr , 5 , stdin ) ; // puts(arr); fputs ( arr , stdout ) ; return 0 ; }
fputs()是puts()的文件操作版本,但fputs()不会自动输出一个\n
打印/输出 printf()、scanf()
# include <string.h> int main ( void ) { char arr [ 100 ] ; scanf ( "%*d%s" , arr ) ; // 屏蔽字符串(开头) printf ( "%s\n" , arr ) ; return 0 ;

长度 strlen()
size_t strlen(const char *s);
计算指定字符串s的长度,不包含字符串结束符 \0
参数
s:字符串首地址
返回值
字符串s的长度,size_t为unsigned int类型
# include <stdio.h> # include <string.h> int main ( void ) { char arr [ ] = "abcdefg" ; int n = strlen ( arr ) ; printf ( "n=%d" , n ) ; return 0 ; }
strcpy()
char *strcpy(char *dest, const char *src);
把src所指向的字符串复制到dest所指向的空间中, '\0' 也会拷贝过去
参数
dest:目的字符串首地址
src:源字符首地址
返回值
成功:返回dest字符串的首地址
失败:NULL
注意: 如果参数dest所指的内存空间不够大,可能会造成缓冲溢出的错误情况。
# include <stdio.h> # include <string.h> int main ( void ) { char dest [ 20 ] = "123" ; char src [ ] = "hello\0 world" ; if ( strcpy ( dest , src ) != NULL ) { printf ( "拷贝成功!\n" ) ; } printf ( "%s\n" , dest ) ; return 0 ; }

strcat()
char *strcat(char *dest, const char *src);
将src字符串连接到dest的尾部, \0 也会追加过去
参数
dest:目的字符串首地址
src:源字符串首地址
返回值
成功:返回dest字符串的首地址
失败:NULL
# include <stdio.h> # include <string.h> int main ( void ) { char arr1 [ ] = "hello" ; char arr2 [ ] = "world" ; strcat ( arr1 , " " ) ; strcat ( arr1 , arr2 ) ; printf ( "%s\n" , arr1 ) ; printf ( "%s\n" , arr2 ) ; return 0 ; }
strcmp()
比较两个字符串的大小, 比较的是字符ASCII码大小
参数
s1:字符串1首地址
s2:字符串2首地址
返回值
相等:0
大于:>0 在不同操作系统strcmp结果会不同,返回ASCII差值
小于:<0
# include <stdio.h> # include <string.h> int main ( void ) { char * str1 = "hello world" ; char * str2 = "hello mike" ; if ( strcmp ( str1 , str2 ) == 0 ) { printf ( "str==str2\n" ) ; } else if ( strcmp ( str1 , str2 ) < 0 ) { printf ( "str<str2\n" ) ; } else if ( strcmp ( str1 , str2 ) > 0 ) { printf ( "str>str2\n" ) ; } return 0 ; }

模板输入/输出 sprintf()、sscanf()
int sprintf(char *_CRT_SECURE_NO_WARNINGS, const char *format, ...);
根据参数format字符串来转换并格式化数据,然后将结果输出到str指定的空间中,直到出现 '\0' 为止。
参数
str:字符串首地址
format:字符串格式,用法和printf()一样
返回值
成功:实际格式化的字符个数
失败:-1
# include <stdio.h> # include <string.h> int main ( void ) { char dst [ 100 ] = { 0 } ; int a = 10 ; char src [ ] = "hello world" ; printf ( "a=%d, src=%s\n" , a , src ) ; int len = sprintf ( dst , "a=%d, src=%s" , a , src ) ; printf ( "dst=\"%s\"\n" , dst ) ; printf ( "len=%d\n" , len ) ; return 0 ; }

查找 strchr()
char *strchr(const char *s, int c);
在字符串s中查找字母c出现的位置
参数
s:字符串首地址
c:匹配字母(字符)
返回值
成功:返回第一次出现的c地址
失败:NULL
# include <stdio.h> # include <string.h> int main ( void ) { char src [ ] = "dddaaa123" ; char * p = strchr ( src , 'a' ) ; printf ( "p=%s\n" , p ) ; return 0 ; }

替换 strtok()
char *strtok(char *str, const char *delim);
将字符串分割成一个个片段。当strtok()在参数str的字符串中发现参数delim中包含的分割字符时,则会将该字符改为0字符,当连续出现多个时只替换第一个为0。
参数
str:指向欲分割的字符串
delim:为分割字符串中包含的所有字符
返回值
成功:分割后字符串首地址
失败:NULL
在第一次调用时,strtok()必须给予参数str字符串
往后的调用则将参数str设置成NULL,每次调用成功则返回被分割出来的片段的指针
# include <stdio.h> # include <string.h> int main ( void ) { char a [ 100 ] = "aaa*bbb*ccc*ddd" ; char * s = strtok ( a , "*" ) ; // 将"*"分割的子串取出 while ( s != NULL ) { printf ( "%s\n" , s ) ; s = strtok ( NULL , "*" ) ; } return 0 ; }

扫描 atoi()
int atoi(const char *nptr);
atoi()会扫描nptr字符串,跳过前面的空格字符,直到遇到数字或正负号才开始做转换,而遇到非数字或字符串结束符( '\0' )才结束转换,并将结果返回返回值。
参数
nptr:待转换的字符串
返回值:成功转换后整数
类似的函数有:
- atof():把一个小数形式的字符串转化为一个浮点数
- atol():将一个字符串转化为long类型
要引入 stdlib.h 依赖,否则可能出现值异常
# include <stdio.h> # include <string.h> # include <stdlib.h> int main ( void ) { char * str1 = "-10a" ; int num1 = atoi ( str1 ) ; printf ( "num1=%d\n" , num1 ) ; char str2 [ ] = "3.141123" ; float num2 = atof ( str2 ) ; printf ( "num2=%f\n" , num2 ) ; return 0 ; }

函数的创建和定义
c程序是由函数组成的(函数是C程序的基本模块,是用于完成特定任务的程序代码单元)。
从函数定义的角度看,函数可分为系统函数和用户定义函数
# include <stdio.h> # include <string.h> # include <stdlib.h> void show ( int arr [ ] , int len ) { for ( int i = 0 ; i < len ; i ++ ) { printf ( "%d " , arr [ i ] ) ; } printf ( "\n" ) ; } void bubble ( int arr [ ] , int len ) { for ( int i = 0 ; i < len ; i ++ ) { for ( int j = i ; j < len ; j ++ ) { int a = arr [ i ] ; int b = arr [ j ] ; if ( a > b ) { arr [ i ] = b ; arr [ j ] = a ; } } } } int main ( void ) { int arr [ ] = { 1 , 3 , 2 , 4 , 7 , 6 , 5 , 8 , 9 , 0 } ; int len = sizeof ( arr ) / sizeof ( arr [ 0 ] ) ; printf ( "%d\n" , len ) ; show ( arr , len ) ; bubble ( arr , len ) ; show ( arr , len ) ; return 0 ; }

函数的声明和调用
#include <stdio.h> // 1、声明 声明函数的格式
extern int add ( int a, int b ) ; int main ( void ) { // 3、调用
int a = add ( 10, 20 ) ; printf ( "%d\n" , a ) ; return 0 ; } // 2、定义 函数的定义就是对函数功能的实现
int add ( int a, int b ) { return a + b ; }
头文件
头文件的作用
- 函数、变量的声明
- 系统库的调用
// 头文件只包含一次 # pragma once # include <stdio.h> # include <string.h> # include <stdlib.h> extern int max ( int a , int b ) ;
为了避免同一个文件被include多次,C/C++中有两种方式,一种是
#ifndef方式,一种是#pragma once方式
方法一:# ifndef __SOMEFILE_H__ # define __SOMEFILE_H__ // 声明语句 # endif方法二:
# pragma once // 声明语句
main函数的参数
# include <stdio.h> int main ( int argc , char * argv [ ] ) { // argc 接收传递参数的个数 // argv 接收传递参数的内容 printf ( "%d\n" , argc ) ; for ( int i = 0 ; i < argc ; i ++ ) { printf ( "%s\n" , argv [ i ] ) ; } return 0 ; }






浙公网安备 33010602011771号