Skip to content

数组

我们把这样的一组数据的集合称为数组(Array),它所包含的每一个数据叫做数组元素(Element),所包含的数据的个数称为数组长度(Length),例如int a[4];就定义了一个长度为4的整型数组,名字是a

数组中的每个元素都有一个序号,这个序号从0开始,而不是从我们熟悉的1开始,称为下标(Index)。使用数组元素时,指明下标即可,形式为:

c
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的十六

下表列出了关于标准整数类型的存储大小和值范围的细节:

类型存储大小值范围
char1 字节-128 到 127 或 0 到 255
unsigned char1 字节0 到 255
signed char1 字节-128 到 127
int2 或 4 字节-32,768 到 32,767 或 -2,147,483,648 到 2,147,483,647
unsigned int2 或 4 字节0 到 65,535 或 0 到 4,294,967,295
short2 字节-32,768 到 32,767
unsigned short2 字节0 到 65,535
long4 字节-2,147,483,648 到 2,147,483,647
unsigned long4 字节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为四舍五入后小数点后的位数

请看下面的代码:

c
#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()

c
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
c
### 若有定义: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 循环

while0)中的0表示假,这个while总是不成立的

while1)始终成立

1. 格式化占位符的语法

%[flags][width][.precision][length]specifier

2. specifier的内容

specifier(转换字符,必选)的内容及含义如下:

转换字符参数类型;转换结果
cchar;字符 getchar()
dint;有符号十进制整数
i同上
edouble;以指数形式输出单、双精度浮点数(小写 e)
E同上(大写 E)
fdouble;以小数形式输出单、双精度浮点数
gdouble;以 %f 或 %e 中较短的输出宽度输出单、双精度浮点数(指数显示小写 e)
G同上(指数显示大写 E)
ounsigned int;无符号八进制(无前导 0)
schar *;字符串
uint;无符号十进制
xunsigned int;无符号十六进制(无前导 0x)
X同上(无前导 0X)
pvoid *;指针值
nint *;存放已写字符的个数
%不进行参数转换;% 自身

注:如果 % 后边的字符不是转换字符,则其行为没有定义。

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 类型输出
c
设一个数为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 循环

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)

相当于:

c
a!=b;
b!=c;
if (a!=c)

(2)例子二:

c
if (a=1,b=2,c>2)

相当于

c
a=1;
b=2;
if(c>2)

也就是说,计算前两个逗号前的式子,而以最后一个式子做返回值标准。

c
#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时不再循环。

c
while (scanf("%d", &n),n)
{

}

C 语言整数与字符串的相互转换

image-20211026220806206

EOF

是一个计算机术语,为End Of File的缩写,在操作系统中表示资料源无更多的资料可读取。资料源通常称为档案或串流。通常在文本的最后存在此字符表示资料结束。

c
  //字符串版
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来说是假,不会继续

读取并处理

c
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
c
/* 三元运算符实例 */
   a = 10;
   b = (a == 1) ? 20: 30;
   printf( "b 的值是 %d\n", b );
c
输入一个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语言对数组元素进行排序(冒泡排序法)

c
%s 是一串字符
%c 是一个字符
s
gets()//读取到回车

scanf()//读取到空格

getchar()//读取一个字符

char arr[100];
strlen(arr)//取字符串长度

冒泡排序

Qsort排序

数组冒泡排序

c
# 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;
}

字符串冒泡排序

c
  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 指针

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;
}
c
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 个字节。

Released under the MIT License.