手撕C语言理论知识(上)粗略讲解C语言的部分入门知识

我的主页一只认真写代码的程序猿
欢迎您来!希望点赞、收藏、评论、加关注
本文章是关于C语言的入门知识及细节的讲解

收录于专栏【C语言的学习


目录

C语言的一些基础知识

操作符简介

Scanf的%[ ]

语句(分支、循环、goto)

 函数

数组



C语言的一些基础知识

  • 主函数 - 程序的入口 - main函数有且仅有一个。
  • char - short - int - long - long long - float - double
  • %d - 十进制整型   %u - 无符号整型
    %c - 字符  %s - 字符串
    %ld - long类型
    %p - 地址
    %f - float类型  %lf - double类型
    %e - 指数形式输出实数
    %o - 八进制  %x - 十六进制输出整数 / 字符串地址
    %g自动选择f和e格式且不输出无意义的0
  • “%-nd”,左对齐,m如果小于实际长度,则忽略
    “%n.xs" 输出n个字符,取字符串的前x位,左边补空格
    "%n.mf"总长度位n,小数点后有m位 在编译器上打印时,小数点后输出6个0
  • 数据类型
    char   字符数据类型1字节
    short  2字节
    int       4字节
    long    4字节/8字节,取决于平台( C标准规定sizeof(long) > sizeof(int) )
    long long   8字节
    float     单精度, 占4字节,32位,六位小数+小数点
    double 双精度,占8字节,64位,有效位数为16位,15小数+小数点
  • bit - 比特 - 最小单位                                                                                                          byte - 字节 - 1个字节是8个比特位,可以放8个二进制位                                                  1kb - 1024byte
  • float x = 9.6; 编译器会默认小数是double类型 -->  float x = 9.6f;
  • 局部变量的作用域(scope)是它所在的代码块,也就是它所在的{}内。                          (局部变量,是指在函数内部或复合语句内部定义的变量)                                              如果全局变量和局部变量同名,那么局部变量优先。全局变量的作用域是整个工程(另一处使用时只需extern声明一下就可以了)全局变量之所以能在其他.c中使用,因为它有外部链接属性,但是被static修饰之后,变成了内部链接属性,其他.c文件不能链接到
    这个静态的全局变量了。
  • 常量有:
    1、字面常量
    2、const修饰的常变量
    3、#define定义的标识符常量: #define Max 10
    4、枚举常量

字符串:由双引号(Double Quote)引起来的一串字符称为字符串字面值(String Literal),或者简称字符串。
注:字符串的结束标志是一个 \0 的转义字符。在计算字符串长度的时候 \0 是结束标志,不算作字符串内容
“”是一个空字符串
字符数组——数组是一组相同类型的元素
char arr[]={ 'a','b','c' ,'\0'或0}

'\0' --> 转义字符 - 0 - ASCII是0        0 --> 数字0        '0' --> 字符0,ASCII是48

转义字符作用
\n换行
\r回车
\t水平制表符
\v垂直制表符
\dddddd表示1~3个八进制的数字
\xdddd表示2个十六进制数字
\?书写连续多个问号时使用,防止解析成三字母词(??+char)
\'字符常量'
\“表示一个字符串内部的双引号
\\表示一个反斜杠,防止它被解释为一个转义序列符
\a警告字符,蜂鸣
\b退格符
\f进纸符

16进制:数字0、1、2、3、4、5、6、7、8、9和字母A、B、C、D、E、F(a、b、c、d、e、f)
printf("%d",'\130'); // 输出8
printf("%d",'\x30');// 输出48

printf("%d\n", strlen("\628\test.c:\testc"));    //输出14
printf("%d\n", strlen("\\628\\test.c:\\testc")); //输出18

输出单个斜杠:
printf("%c",' ' ');//error,前面两个变成一对,后面落单
printf("%c",'\'');//正确


操作符简介

算术操作符:加减乘除模

移位操作符:左移<< 和 右移>>

位操作符:与&(相同才为1)、或|(有1就1)、异或^(相同则0)

赋值操作符:=    +=    -=    *=    /=    &=    ^=     |=      >>=     <<=

单目操作符:
!           逻辑反操作
-           负值
+           正值
&           取地址
sizeof        操作数的类型长度(以字节为单位)
~               对一个数的二进制按位取反
--               前置、后置--
++             前置、后置++
*                间接访问操作符(解引用操作符)
(类型)        强制类型转换

关系操作符:
>
>=
<
<=
!=   测试不相等
==      测试相等

逻辑操作符:与&&(遇到假就停止,后面表达式不执行),或||(遇到真就停止)

条件操作符: exp1?exp2:exp3

逗号表达式: exp1,exp2,...expn

下标引用:[]        函数调用:()        结构成员: . ->

sizeof计算 变量/类型 所占空间大小,单位字节。计算类型时必须加括号
int a = 10;        int arr[10]={0}; 
printf("%d\n",sizeof(int));//4
printf("%d\n",sizeof(a));//4
printf("%d\n",sizeof a);//4
printf("%d\n",sizeof int);//error

printf("%d\n",sizeof arr);//40

按位取反~
int a=0; int b=~a;
printf("%d",b);//输出-1
负数在内存中存储的时候,是二进制的补码。使用时,打印的是这个数的原码(原,反,补)


常见关键字:auto  break   case  char  const   continue  default  do   double else  enum   extern float  for   goto  if   int   long  register    return   short  signed sizeof   static struct  switch  typedef union  unsigned   void  volatile  while

auto:现在一般都省略了,auto int a =10;
register:建议把某个变量放到寄存器内,是否存放进去由编译器决定。
signed:有符号数,signed int 和 int是等价的
typedef:类型重定义(typedef unsigned int u_int;        u_int a=5;)
static:
1、修饰局部变量 - 使局部变量的生命周期变长
2、修饰全局变量 - 改变全局变量的作用域 - 使静态的全局变量只能在自己所在源文件内部使用
3、修饰函数 - 改变了函数的链接属性,使其变成了内部链接属性(正常的函数有外部链接属性)


 #define定义标识符常量和宏:
#define MAX 100
#define  ADD(x,y)  (x+y)
宏定义中的宏名一般用全大写用于区分,宏定义不是C语言语句,不要加分号!

#define命令出现在程序中函数的外面(不能在函数内)作用域:定义命令到源文件结束。
#undef命令可以结束宏定义(#undef MAX)这不是语句,没有分号
宏定义只是预处理指令,只做字符的替换,而不分配内存空间

函数结果有无括号是有区别的。

如果字符串中含有宏名,不进行替换。


指针

指针变量的大小取决于地址的大小
32位平台下地址是32个bit位(即4个字节)
64位平台下地址是64个bit位(即8个字节)
int *pa = &a;指针的名字是p,类型是int*


结构体

结构体中字符串不能直接进行修改,而是使用strcpy函数

#include <stdio.h>
#include <string.h>
struct book
{
    char name[20];
    int price;
};
int main()
{
    struct book b={"C语言设计",20};
    b.price=10;
    strcpy(b.name,"C语言书籍");
    printf("%d\n",b.price);
    printf("%s",b.name);
}

Scanf的%[ ]

在输入字符时,scanf 遇到空格、回车等结束符都是会读取的。

除了加一个getchar()之外,另一个方法就是scanf的%[]


    scanf("%[^\n]", arr1);//回车之前的字符串都读取
	scanf("%[a-z]", arr2);//只读取a-z的字符,碰到非此字符及停止
    //例如:arr2输入abcd123,只读取abcd


语句(分支、循环、goto)

C语言中由一个分号隔开就是一条语句
1. 表达式语句
2. 函数调用语句
3. 控制语句
4. 复合语句
5. 空语句

 

九个控制语句:

1. 条件判断语句(分支语句):if、switch;
2. 循环执行语句:do while、while、for;
3. 转向语句:break、goto、continue、return

分支语句(选择结构)

if语句条件判断用“ == ”而不是 “ = ”。
建议把常量写在左边:if ( n==0 ) 写成 if ( 0==n )
switch用于多分支情况,case后面必须跟整型,且中间有空格。
switch-case-break-default,实际上default可以随便放哪,只是一般放在最后。
continue语句是不可以在单独的switch语句中使用,但可以在一个循环内的switch语句中使用

 循环语句for、while、do while

while ( (ch=getchar()) != EOF) 输入EOF,则ch=E,windows系统下,结束循环需要按下Ctrl+z
EOF的宏定义是#define EOF -1

由于ASCII码值的范围是0~255,所以,可以用EOF作为文件(这里一定要求是文本文件)结束标志,或是作为输入结束标志。

在C语言里,NULL代表的是值为0的void型指针,称为空地址,但NULL也有地址,它的作用是用来防止出现野指针的。 可以将NULL作为空指针常量使用,也可以写成 int*p=0;

for(初始化,判断,调整),for循环内的continue是跳到调整部分;for循环中如果判断部分省略,则判断部分恒为真。

别忘了do-while之后的分号 “ ; ”

 计算前n项的n阶和(忘记重置)

#include <stdio.h>
int main()
{
    int n =0;
    scanf("%d",&n);
    int sum=1;
    int s=0;
    for(int j=1;j<n+1;j++)
    {
        sum=1;//每次循环之后要记得重置(易错点)
        for(int i=1;i<j+1;i++)
        {
            sum*=i;
        }
        s+=sum;
    }
    printf("%d",s);
}

 二分查找

void binary_search(int x,int*arr,int sz)
{
    int left=0;
    int right=sz-1;
    while(left<=right)
    {
        int mid = (left+right)/2;
        if(arr[mid]>x)
        {
            right=mid-1;
        }
        else if(arr[mid]<x)
        {
            left=mid+1;
        }else 
        {
            printf("找到了,下标是%d",mid);
            break;
        }
    }
    if(left>right) printf("找不到");
}

Rand&Srand

rand()函数每次调用前都会查询是否调用过srand(seed),如果有,会自动调srand(seed)来初始化它的起始值;如果没有,系统会自动给seed赋初始值,即srand(1)自动调用它一次。一旦种子相同,每次的随机数也就相同,所以用到时间戳给srand。

时间戳:当前时间 - 计算机起始时间(1970.1.1.0:00)换算成秒
srand(time(NULL))
代码:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main()

{
srand((unsigned int)time(0));
int i = 0;
for(;i < 5;++i)
   {
        int num = rand() % 100 + 1;
        printf("%d ",num);
    }
printf("\n");
}

srand函数在工程里只要调用一次就行了,不要把它放到自定义函数里多次调用。

goto语句

goto语句和标记跳转的标号是可以随意滥用的,但是除非是不得已的情况,不然不建议使用。goto语句最常见的用法是一次性跳出多层循环。

#include <stdio.h>

#include <string.h>

#include <windows.h>

int main()

{

    char input[15]={0};

    system("shutdown -s -t 60");

    again:

    printf("电脑将在60后关机,输入我是笨蛋就取消关机:>\n");

    scanf("%s",input);

    if(0==strcmp(input,"我是笨蛋"))

        system("shutdown -a");

    else

        goto again;

}//可以用while循环替代这一整段代码


 函数

常用库函数:IO函数、字符串操作函数、内存操作函数、时间函数、数学函数、其他

函数参数:
实际参数(实参):真实传给函数的参数。可以是常量、变量、表达式、函数等。无论实参是什么类型,它都必须有确定的值,以便于把值传给形参。
形式参数(形参):函数名后面括号里的变量,之所以叫形参是因为只有在函数被调用的过程中才实例化(分配内存),因此,形参只有在函数中才有效。
在交换函数中,可以理解为形参只是实参的一份临时拷贝,改变形参不会改变实参。

函数调用:
传值调用:函数的形参和实参分别占不同内存块,修改形参不改变实参。
传址调用:把函数外部创建变量的内存地址传给函数参数。这种传参方式可以让函数和函数外的变量建立起真正的联系。

注意:不能在函数内部计算数组元素个数,因为传过去的数组名是指针变量。
在函数中的sizeof(arr)/sizeof(arr[0])始终为1

嵌套调用与链式访问:
嵌套是指在应该函数中调用另一个函数;链式是指把一个函数的返回值作为另一个函数的参数。

printf("%d",printf("%d",printf("%d",43)));
输出4321
printf("%d",printf("%d",printf("%s","ab")));
输出ab21
printf的返回值是打印的字符个数

函数的声明与定义:
函数声明一般出现在函数使用之前,先声明后使用;函数声明一般放在头文件.h中。
函数定义是指函数的具体实现、函数功能实现 

函数递归
栈溢出(stac overflow):栈区空间一般是局部变量、函数形参等;堆区空间动态内存开辟;静态区一般是全局变量、static。当函数不停地调用自己的时候,会不停在栈区占用空间,最终导致栈溢出。
递归需要存在限制条件,当条件成立时递归结束;同时每次递归调用之后都要不停接近这个条件。递归的优缺点:结构清晰,代码简洁;但是可能会导致栈溢出、产生很多重复计算导致效率低、不停地调用函数需要消耗时间和空间导致效率低。

正序打印一个整型数字的每一位、不创建临时变量求字符串长度、求n!、计算第n个斐波那契数、汉诺塔、青蛙跳台阶....


数组

创建数组时,方括号里只能是常量,不能是变量。
C89标准:创建数组时,[ ]内容必须是常量。
C99标准:[ ]内可以使用变量。
不完全初始化:int n[5]={1,2,3};后面元素默认为0
sizeof()计算变量、数组、类型的大小,单位是字节 - 操作符
strlen()求字符串长度,针对字符串 - 库函数

零碎知识点:

1、 一维数组和二维数组在内存中都是连续存放的
2、二维数组初始化:行可以省略,列不能省略( int arr[ ][4] )
当你省略了行数,确定了列数,计算机会根据你的列数以及你初始化时设定的数据,自动确定行数。如果你省略了列数,编译器的寻址方式是array+ n * i + j(第n行第j列),由于没有行数,无法确定i的大小,所以计算机此时不知道该如何分配数据。
3、二维数组就当做多个一维数组来理解就好了。
4、千万不要在自定义函数内部计算数组的个数,计算值始终为1
void fac(int*arr)
{int sz=sizeof(arr)/sizeof(arr[0]);}
由于传过来的是首元素地址,所以二者都是指针,大小为4/8,相除为1.
5、数组名是首元素地址(两个例外如下)

sizeof(数组名),计算整个数组的大小,sizeof内部单独放一个数组名,数组名表示整个数组。
&数组名,取出的是数组的地址。&数组名,数组名表示整个数组。

  • 21
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 24
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 24
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值