数组
我们把这样的一组数据的集合称为数组(Array),它所包含的每一个数据叫做数组元素(Element),所包含的数据的个数称为数组长度(Length),例如int a[4];
就定义了一个长度为4的整型数组,名字是a
。
数组中的每个元素都有一个序号,这个序号从0开始,而不是从我们熟悉的1开始,称为下标(Index)。使用数组元素时,指明下标即可,形式为:
arrayName[index]
array Name 为数组名称,index 为下标。例如,a[0] 表示第0个元素,a[3] 表示第3个元素。
C 字符串
在 C 语言中,字符串实际上是使用空字符 \0 结尾的一维字符数组。因此,\0 是用于标记字符串的结束。
空字符(Null character)又称结束符,缩写 NUL,是一个数值为 0 的控制字符,\0 是转义字符,意思是告诉编译器,这不是字符 0,而是空字符。
下面的声明和初始化创建了一个 RUNOOB 字符串。由于在数组的末尾存储了空字符 \0,所以字符数组的大小比单词 RUNOOB 的字符数多一个。
char site[7] = {'R', 'U', 'N', 'O', 'O', 'B', '\0'};
变量声明可以用 下划线 字母 数字,但不能以数字开头 且区分大小写
数组没有\0结尾,多少数就是多少元素,字符串有\0,所以多少字符+1才是元素个数
但数字是多少就是有多少元素
整数类型
正常的十进制,0的八进制,0x的十六
下表列出了关于标准整数类型的存储大小和值范围的细节:
类型 | 存储大小 | 值范围 |
---|---|---|
char | 1 字节 | -128 到 127 或 0 到 255 |
unsigned char | 1 字节 | 0 到 255 |
signed char | 1 字节 | -128 到 127 |
int | 2 或 4 字节 | -32,768 到 32,767 或 -2,147,483,648 到 2,147,483,647 |
unsigned int | 2 或 4 字节 | 0 到 65,535 或 0 到 4,294,967,295 |
short | 2 字节 | -32,768 到 32,767 |
unsigned short | 2 字节 | 0 到 65,535 |
long | 4 字节 | -2,147,483,648 到 2,147,483,647 |
unsigned long | 4 字节 | 0 到 4,294,967,295 |
注意,各种类型的存储大小与系统位数有关,但目前通用的以64位系统为主。
以下列出了32位系统与64位系统的存储大小的差别(windows 相同):
char 变量在内存中存储的是字符对应的 ASCII 码值。如果以 %c 输出,会根据 ASCII 码表转换成对应的字符;如果以 %d 输出,那么还是整数。
转义字符
int 变量在内存中存储的是整数本身,当以 %c 输出时,也会根据 ASCII 码表转换成对应的字符。
转义字符 | 意义 | ASCII码值(十进制) |
---|---|---|
\a | 响铃(BEL) | 007 |
\b | 退格(BS) ,将当前位置移到前一列 | 008 |
\f | 换页(FF),将当前位置移到下页开头 | 012 |
\n | 换行(LF) ,将当前位置移到下一行开头 | 010 |
\r | 回车(CR) ,将当前位置移到本行开头 | 013 |
\t | 水平制表(HT) | 009 |
\v | 垂直制表(VT) | 011 |
' | 单引号 | 039 |
" | 双引号 | 034 |
\ | 反斜杠 | 092 |
对除法的说明
C语言中的除法运算有点奇怪,不同类型的除数和被除数会导致不同类型的运算结果:
- 当除数和被除数都是整数时,运算结果也是整数;如果不能整除,那么就直接丢掉小数部分,只保留整数部分,这跟将小数赋值给整数类型是一个道理。
- 一旦除数和被除数中有一个是小数,那么运算结果也是小数,并且是 double 类型的小数。
- printf("%f.n",&a) n为四舍五入后小数点后的位数
请看下面的代码:
#include <stdio.h>
int main()
{
int a = 100;
int b = 12;
float c = 12.0;
double p = a / b;
double q = a / c;
printf("p=%lf, q=%lf\n", p, q);
return 0;
}
运行结果: p=8.000000, q=8.333333
a 和 b 都是整数,a / b 的结果也是整数,所以赋值给 p 变量的也是一个整数,这个整数就是 8。
另外需要注意的一点是除数不能为 0,因为任何一个数字除以 0 都没有意义。
然而,编译器对这个错误一般无能为力,很多情况下,编译器在编译阶段根本无法计算出除数的值,不能进行有效预测,“除数为 0”这个错误只能等到程序运行后才能发现,而程序一旦在运行阶段出现任何错误,只能有一个结果,那就是崩溃,并被操作系统终止运行。
先说a=i++,这个运算的意思是先把i的值赋予a,然后在执行a+1;i不变
而a=++i,这个的意思是先执行i=i+1,然后在把i的值赋予a;i值会加一
赋值运算符
下表列出了 C 语言支持的赋值运算符:
运算符 | 描述 | 实例 |
---|---|---|
= | 简单的赋值运算符,把右边操作数的值赋给左边操作数 | C = A + B 将把 A + B 的值赋给 C |
+= | 加且赋值运算符,把右边操作数加上左边操作数的结果赋值给左边操作数 | C += A 相当于 C = C + A |
-= | 减且赋值运算符,把左边操作数减去右边操作数的结果赋值给左边操作数 | C -= A 相当于 C = C - A |
*= | 乘且赋值运算符,把右边操作数乘以左边操作数的结果赋值给左边操作数 | C *= A 相当于 C = C * A |
/= | 除且赋值运算符,把左边操作数除以右边操作数的结果赋值给左边操作数 | C /= A 相当于 C = C / A |
%= | 求模且赋值运算符,求两个操作数的模赋值给左边操作数 | C %= A 相当于 C = C % A |
<<= | 左移且赋值运算符 | C <<= 2 等同于 C = C << 2 |
>>= | 右移且赋值运算符 | C >>= 2 等同于 C = C >> 2 |
&= | 按位与且赋值运算符 | C &= 2 等同于 C = C & 2 |
^= | 按位异或且赋值运算符 | C ^= 2 等同于 C = C ^ 2 |
|= | 按位或且赋值运算符 | C |= 2 等同于 C = C | 2 |
C 库函数 - scanf()
char str[100]
//读取到\n为止,如果其中有空格一样读取,scanf字符串的时候不用取地址符.
scanf("%[^\n]s",str);
//读取字符需要取地址符.
scanf("%c",&ch);
printf、scanf占位符
C if...else 语句
C 运算符
算数运算。运算符左右两边的类型必须一样
如果遇到不一样的,编译器会转换成一样的再计算。
转换的话会尽量取“大”的那一个
比如
int / long = long
int + double = double
int / int =int
float + int = float
然后算出来结果的类型也会和参与计算的两个数一样
比如 int / long 会转换成 long / long
然后 long / long 的结果 也只能是long,不可能是float或者double
同理double + float会转换成 double + double,计算出来的结果是 double。即使加完以后小数位刚好没有,也会是double,不会是int
### 若有定义:int a=8,b=5,c;,执行语句c=a/b+0.4;后,c的值为
- A. 1.4
- B. 1
- C. 2.0
- D. 2
*正确答案:* B
实例
#include sstdio.h>
#include <math.h>
int main(void)
float m:
m = 1/2;
printf("%.3f",m)
return 0;
这样打印出来的m值是0.000,是因为1/2都是int,所以m的小数点后的直接舍弃了
C do...while 循环
while(0)中的0表示假,这个while总是不成立的
while(1)始终成立
1. 格式化占位符的语法
%[flags][width][.precision][length]specifier
2. specifier的内容
specifier(转换字符,必选)的内容及含义如下:
转换字符 | 参数类型;转换结果 |
---|---|
c | char;字符 getchar() |
d | int;有符号十进制整数 |
i | 同上 |
e | double;以指数形式输出单、双精度浮点数(小写 e) |
E | 同上(大写 E) |
f | double;以小数形式输出单、双精度浮点数 |
g | double;以 %f 或 %e 中较短的输出宽度输出单、双精度浮点数(指数显示小写 e) |
G | 同上(指数显示大写 E) |
o | unsigned int;无符号八进制(无前导 0) |
s | char *;字符串 |
u | int;无符号十进制 |
x | unsigned int;无符号十六进制(无前导 0x) |
X | 同上(无前导 0X) |
p | void *;指针值 |
n | int *;存放已写字符的个数 |
% | 不进行参数转换;% 自身 |
注:如果 % 后边的字符不是转换字符,则其行为没有定义。
flags(标志,可选)的内容即含义如下:
标志 | 含义 |
---|---|
- | 指定被转换的参数在其字段内左对齐(默认是右对齐) |
+ | 指定在输出的数前面加上正负号 |
空格 | 如果第一个字符不是正负号,则在其前面加上一个空格 |
0 | 对于数值转换,当输出长度小于字段宽度时,添加前导 0 进行填充 |
# | 指定另一种输出形式: 1. 如果转换字符为 o,则第一个数字为 0 2. 如果转换字符为 x 或 X,则指定在输出的非 0 值前加 0x 或 0X 3. 对于转换字符为 e、E、f、g 或 G 的情况,指定输出总是包含一个小数点。另外,对于转换字符为 g 或 G,还指定输出值尾部无意义的 0 将被保留 |
注:flags 可同时出现多个,且无顺序要求。
width(宽度,可选)是一个数值,用于指定最小字段的宽度
- 转换后的参数输出宽度至少要达到这个数值。如果参数的字符数小于该数值,则在参数左边(如果 flags 设置为 -,要求左对齐的话则在右边)填充一些字符。填充字符通常为空格,但是如果 flags 设置为 0,则填充字符为数字 0。
.precision(.精度,可选),通过点号(.)分隔字段的宽度和精度
- 对于字符串,它指定打印的字符的最大个数
- 对于整数,它指定打印的数字位数(必要时可加填充位 0 以达到宽度要求)
- 对于转换字符为 e、E 或 f,它指定打印的小数点后的数字位数
- 对于转换字符为 g 或 G,它指定打印的有效数字位数
length(长度修饰符,可选)的值可以是 h、hh、l、ll 或 L
- hh 表示将相应的参数按 signed char 或 unsigned char 类型输出
- h 表示将相应的参数按 short 或 unsigned short 类型输出
- l 表示将相应的参数按 long 或 unsigned long 类型输出
- ll 表示将相应的参数按 long long 或 unsigned long long 类型输出
- L 表示将相应的参数按 long double 类型输出
设一个数为n,则在C语言中其个位、十位、百位、千位依次这样计算:n/1%10,n/10%10,n/100%10,n/1000%10
代码如下:
#include<stdio.h>
int main(){
int n = 123456;
int unitPlace = n / 1 % 10;
int tenPlace = n / 10 % 10;
int hundredPlace = n / 100 % 10;
int thousandPlace = n / 1000 % 10;
printf("个位:%d\n十位:%d\n百位:%d\n千位:%d\n", unitPlace, tenPlace, hundredPlace, thousandPlace);
getchar();
return 0;
}
C 循环
#include <stdio.h>
int main ()
{
/* 局部变量定义 */
int a = 10;
/* 先判断za */
/* while 循环执行 */
while( a < 20 )
{
printf("a 的值: %d\n", a);
a++;
}
return 0;
}
if函数多个逗号
说明: 逗号表达式与加减乘除本质上是一样的, 它的求值是从左向右依次对表达式求值, 整个表达式的结果取逗号表达式中最后一个表达的的结果, 如果非零, 就会使 if 成立! (1)例子一:
if (a!=b,b!=c,a!=c)
相当于:
a!=b;
b!=c;
if (a!=c)
(2)例子二:
if (a=1,b=2,c>2)
相当于
a=1;
b=2;
if(c>2)
也就是说,计算前两个逗号前的式子,而以最后一个式子做返回值标准。
#include <stdio.h>
int main ()
{
/* 局部变量定义 */
int a = 10;
/* 先执行再判断 */
/* do 循环执行,在条件被测试之前至少执行一次 */
do
{
printf("a 的值: %d\n", a);
a = a + 1;
}while( a < 20 );
return 0;
}
循环读取,直到n=0
结合上面的解释,即可达到当n=0时不再循环。
while (scanf("%d", &n),n)
{
}
C 语言整数与字符串的相互转换
EOF
是一个计算机术语,为End Of File的缩写,在操作系统中表示资料源无更多的资料可读取。资料源通常称为档案或串流。通常在文本的最后存在此字符表示资料结束。
//字符串版
while (c = getchar(),c != EOF&&c!='\n')
{
}
if (a!=b,b!=c,a!=c)
相当于:a!=b;b!=c;if (a!=c)
//scanf 版
while (scanf("%d%d", &m, &n)!=EOF)
{
}
scanf() 函数有返回值且类型int 型,当发生错误时立刻返回EOF
scanf() 函数返回的值为:正确按指定格式输入变量的个数;也即能正确接收到值的变量个数。
//有条件版
while (scanf("%d",&m)==1&&m!=-1)
{
}
gets (str);
for (int i=0;str[i]!='\0';i++)
{
}
while (gets(a)) oj平台会停下,本地要ctrl+z
while (scanf("%s",a)==1)
scanf如果读取不到数据会返回EOF,但对while来说这个条件属于真,还会继续进行
gets如果读取不到数据会返回null,对while来说是假,不会继续
读取并处理
ch = getchar();
while (ch != EOF) {
while (ch != EOF && ch != '\n'){ //如果是同一行的内容,则持续处理
XXXXXXXXXX
XXXXXXXXXX
XXXXXXXXXX
ch = getchar(); //处理并读取下字符内容
}
printf("\n"); //处理完一行打印一个换行
if (ch != EOF) //并没有结束则进行读取下一行
ch = getchar();
}
}
杂项运算符 ↦ sizeof & 三元
下表列出了 C 语言支持的其他一些重要的运算符,包括 sizeof 和 ? :。
运算符 | 描述 | 实例 |
---|---|---|
sizeof() | 返回变量的大小。 | sizeof(a) 将返回 4,其中 a 是整数。 |
& | 返回变量的地址。 | &a; 将给出变量的实际地址。 |
* | 指向一个变量。 | *a; 将指向一个变量。 |
? : | 条件表达式 | 如果条件为真 ? 则值为 X : 否则值为 Y |
/* 三元运算符实例 */
a = 10;
b = (a == 1) ? 20: 30;
printf( "b 的值是 %d\n", b );
输入一个n,将n个数存入数组
int a[n],n
scanf("%d", &n)
for (i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
}
实例
#include <stdio.h>
int main(void)
{
int n, i, m;
while (scanf("%d", &n), n != 0)
{
int a[n], max = 0;
for (i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
}
for (i = 1; i <= n - 2; i++)
{
if (a[i] + a[i + 1] + a[i + 2] > max)
{
max = a[i] + a[i + 1] + a[i + 2];
}
}
printf("%d\n", max);
}
return 0;
}
C语言对数组元素进行排序(冒泡排序法)
%s 是一串字符
%c 是一个字符
s
gets()//读取到回车
scanf()//读取到空格
getchar()//读取一个字符
char arr[100];
strlen(arr)//取字符串长度
冒泡排序
Qsort排序
数组冒泡排序
# include <stdio.h>
int main(void)
{
int a[] = {900, 2, 3, -58, 34, 76, 32, 43, 56, -70, 35, -234, 532, 543, 2500};
int n; //存放数组a中元素的个数
int i; //比较的轮数
int j; //每轮比较的次数
int buf; //交换数据时用于存放中间数据
n = sizeof(a) / sizeof(a[0]); /*a[0]是int型, 占4字节, 所以总的字节数除以4等于元素的个数*/
for (i=0; i<n-1; i++) //比较n轮
{
for (j=0; j<n-1-i; j++) //每轮比较n-1-i次, 在升序的情况,因为后面都排完了所以可以忽略,如果是降序不行(或者是全部被放到后面)
{
if (a[j] < a[j+1])
{
buf = a[j];
a[j] = a[j+1];
a[j+1] = buf;
}
}
}
for (i=0; i<n; ++i)
{
printf("%d", a[i]);
}
return 0;
}
字符串冒泡排序
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n-i-1; j++)
{
if (strcmp(str[i], str[j]) == 1)
{
char temp[85];
strcpy(&temp, &str[i]);
strcpy(&str[i], &str[j]);
strcpy(&str[j], &temp);
}
}
}
//strcmp 比较字符串大小 前大为>0 相同为0 后大为<0
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
实测对的
c++
#include <iostream>
#include <string>
#include <cstring>
#include <cmath>
using namespace std;
int distance(int a,int b)
{
return sqrt(a*a+b*b);
}
int main()
{
int n,a[100][2];
cin >> n;
for (int i = 0; i < n; i++)
{
cin>>a[i][0]>>a[i][1];
}
for(int i=0;i<n-1;i++)
{
for(int i_one=0;i_one<n-1;i_one++)
{
if(distance(a[i_one][0],a[i_one][1])>distance(a[i_one+1][0],a[i_one+1][1]))
{
int temp[1][2];
memcpy (temp[0],a[i_one],sizeof(a[i_one]));
memcpy (a[i_one],a[i_one+1],sizeof(a[i_one+1]));
memcpy(a[i_one+1],temp[0],sizeof(temp[0]));
}
}
}
for(int i=0;i<n;i++)
{
cout<<"("<<a[i][0]<<","<<a[i][1]<<")"<<" ";
}
return 0;
}
C 指针
#include <stdio.h>
int main ()
{
int var_runoob = 10;
int *p; // 定义指针变量,*p是直接操作p地址的变量值
p = &var_runoob; //p为变量的地址
printf("var_runoob 变量的地址: %p\n", p);
return 0;
}
int main(void)
{
char* insertTime = "2021";
printf("|%-15s|\n",insertTime); //左对齐,15位长度,不够补空格
printf("|%15s|\n",insertTime); //右对齐,15位长度,不够补空格
printf("|%015s|\n",insertTime); //右对齐,15位长度,不够补0
printf("|%-15.2f|\n",atof(insertTime)); //左对齐,15位长度,带两位小数,不够补空格
float f=123.456;
printf("%f \n",f);
printf("%10.1f \n",f);
printf("%5.1f \n",f);
printf("%10.3faaa \n",f);
printf("%-10.3faaa \n",f);
printf("%g \n",f);
return 0;
}
|2021 |
| 2021|
|000000000002021|
|2021.00 |
123.456001
123.5
123.5
123.456aaa
123.456 aaa
123.456
1 char** a
char* 也是一个指针,用 *a 表示,这个指针(即指针 *a)指向一块内存地址,该内存地址中存储的是 ***char 类型*的数据。指针的加减运算在这里的体现为:(*a) + 1 表示地址加 1 字节。
说明:
- 在 32 位系统中,一个指针占用 4 字节(32 位)内存空间;在 64 位系统中,一个指针占用 8 字节(64 位)内存空间;
- 由于 a 指向一个指针类型(char*),故 a + 1 操作就是对指针类型的地址进行操作,所以 a + 1 表示地址加 8 字节;*a指向一个 char 类型,char 类型占用 1 个字节,故 *a + 1 操作就是对 char 类型的地址进行操作,所以 *a + 1 表示地址加 1 字节。
2 char* a[]
在 char* a[] 中,a 是数组,数组中的元素是指针,这些指针指向 char 类型的数据。
说明:
- 数组里面所有的元素,在内存中都是是连续存放的;
- 数组名在 C 语言中做了特殊处理,数组名使用数组所占用的(连续)内存区域的第一个字节的内存地址替代了。例如,数组占用的内存区域是 0x7fff5da3f550 到 0x7fff5da3f5a0,那么数组名 a 就会被替换成首地址 0x7fff5da3f550;
- a+1 表示数组 a 的第二个元素的内存地址,所以 a + 1 是地址加 8 字节(再次说明,因为数组 a 的元素是指针(char*),指针类型占用 8 字节);
- char* a[10] 表示限定这个数组最多可以存放 10 个指针(char*)元素,也就是说这个数组会占用 10 * 8 = 80 个字节。