分类: 技术

  • 一些 C 的细节

    做专业课的题目的时候发现之前虽然写了很多代码但是还有很多细节是比较模糊的,这些细节在考试中还是经常遇到的!

    构成C程序的基本单位是函数

    最小单位:表达式
    基本单位:函数
    编译单位:文件

    字符串和字符数组

    • 对于字符数组,其长度是固定的,其中任何一个数组元素都可以为 null 字符。因此,字符数组不一定是字符串。
    • 对于字符串,它必须以 null 结尾,其后的字符不属于该字符串。字符串一定是字符数组,它是最后一个字符为 null 字符的字符数组。

    函数的声明

    正确: int fun(int, int) 
    在函数声明或定义时,可以不写形参的名字。这是因为编译器在这个阶段主要关心参数的类型和数量,而不是参数的具体名称。例如,int fun(int, int) 是一个 有效 的函数声明,它表明有一个名为 fun 的函数,该函数接受两个 int 类型的参数,并返回一个 int 类型的值。

    错误:int fun(int x, y)
    在C语言中,函数的参数列表中的每个参数都需要声明其类型。在int fun(int x, y)中,y没有声明其类型,这会导致编译错误。

    运算符优先级和结合性

    优先级运算符名称和含义使用型式结合方向种类说明
    1[]数组下标数组名[常量表达式]从左到右特殊运算符
    ()圆括号(表达式)
    函数名(形参表)
    .成员选择(对象)对象.成员
    ->成员选择(指针)对象指针->成员名
    2负号运算符– 表达式从右到左单目运算符
    (类型)强制类型转换(数据类型)表达式
    ++自增运算符++变量名
    变量名++
    算术运算符单目运算符
    自减运算符–变量名
    变量名–
    单目运算符
    *取值运算符*指针变量指针运算符单目运算符
    &取地址运算符&变量名单目运算符
    !逻辑非运算符!表达式逻辑作运算符单目运算符
    ~按位取反运算符~表达式位操作运算符单目运算符
    sizeof长度运算符sizeof(表达式)求字节数运算符
    3/表达式 / 表达式从左到右算术运算符双目运算符
    *表达式*表达式双目运算符
    %余数(取模)整型表达式%整型表达式双目运算符
    4+表达式 + 表达式从左到右双目运算符
    表达式 – 表达式双目运算符
    5<<左移变量<<表达式从左到右位操作运算符双目运算符
    >>右移变量>>表达式双目运算符
    6>大于表达式 > 表达式从左到右关系运算符双目运算符
    >=大于等于表达式 >= 表达式双目运算符
    <小于表达式 < 表达式双目运算符
    <=小于等于表达式 <= 表达式双目运算符
    7==等于表达式 == 表达式双目运算符
    !=不等于表达式 != 表达式双目运算符
    8&按位与表达式 & 表达式从左到右位操作运算符双目运算符
    9^按位异或表达式 ^ 表达式从左到右双目运算符
    10|按位或表达式 | 表达式从左到右双目运算符
    11&&逻辑与表达式 && 表达式从左到右逻辑运算符双目运算符
    12||逻辑或表达式 || 表达式从左到右双目运算符
    13?:条件运算符表达式1? 表达式2: 表达式3从右到左条件运算符三目运算符
    14=赋值运算符变量 = 表达式从右到左赋值运算符
    /=除后赋值变量 /= 表达式
    *=乘后赋值变量 *= 表达式
    %=取模后赋值变量 %= 表达式
    +=加后赋值变量 += 表达式
    -=减后赋值变量 -= 表达式
    <<=左移后赋值变量 <<= 表达式
    >>=右移后赋值变量 >>= 表达式
    &=按位与后赋值变量 &= 表达式
    ^=按位异或后赋值变量 ^= 表达式
    |=按位或后赋值变量 |= 表达式
    15,逗号运算符表达式,表达式,…从左到右逗号运算符

    可变参数

    在C语言中,argcargv是用于命令行参数传递的两个变量。它们通常在main()函数中定义,用于接收从命令行传递给程序的参数。下面是它们的作用和用法:

    • argc(argument count):它表示传递给程序的命令行参数的数量,包括程序名称本身。因此,至少为1。它是一个整数类型的变量。
    • argv(argument vector):它是一个指向字符指针数组的指针,用于存储传递给程序的命令行参数的字符串。每个元素指向一个命令行参数的字符串。数组的最后一个元素后面是一个NULL指针。

    将程序prog.c编译链接后运行:prog -nla hello world则*(*(argv+2))是( )

    A. ‘p’
    B. ‘-‘
    C. ‘h’
    D. ‘w’

    argv+2            //地址加2,相当于argv[2]的地址
    *(argv+2)        //表示字符数组argv[2],同时表示字符串‘hello’的首地址
    *(*(argv+2))    //首地址上的值,也就是‘h’
    
    调试输出:
    PS C:\Users\i\Desktop\Code\cpp> ./test.exe  -nla hello world
    4
    argv[0] = C:\Users\i\Desktop\Code\cpp\test.exe
    argv[1] = -nla
    argv[2] = hello
    argv[3] = world
    *(*(argv+2)) = h

    存储类型 register 和 extem

    在C语言中,registerextern是两个不同的关键字,具有不同的用途。

    1. register关键字:
      • register是C语言中的一个存储类说明符,它建议编译器将某个局部变量存储在寄存器中,而不是在RAM中,从而加快对该变量的访问速度。
      • 使用register关键字的局部变量被称为寄存器变量。
      • 请注意,register只是一个建议,编译器可以选择忽略它。现代编译器通常能够自动选择将哪些变量放入寄存器,因此register关键字在现代C语言编程中不太常用。示例:
    register int counter;
    1. extern关键字:
      • extern用于声明一个变量或函数是在别的文件中定义的,而不是在当前文件中定义的。
      • 通过使用extern,你可以在当前文件中引用在其他文件中定义的变量或函数。
      • extern通常用于多文件编程,以实现变量和函数的跨文件访问。示例:
    // file1.c
    int globalVar = 10;
    
    // file2.c
    extern int globalVar; // 使用extern来引用在file1.c中定义的globalVar

    所有的数据都有两种类型,一是常见的数据类型,如int,float等,一种便是存储类型。总共有四种存储类型的变量,分别为自动变量(auto)、静态变量(static)、外部变量(extern)以及寄存器变量(register)。

    定义:

    1、自动变量:函数中所有的非静态局部变量。
    2、静态变量:在变量前加上static关键字的变量。
    3、外部变量:一般用作全局变量作用域的扩展(还有定义外部函数的时候前面也可以有一个extern关键字,具体外部函数请看下面)。
    4、寄存器变量:一般经常被使用的的变量(如某一变量需要计算几千次)可以设置成寄存器变量,register变量会被存储在寄存器中,计算速度远快于存在内存中的非register变量。

    存储变量关乎着变量的作用域和生存时间

    1、从作用域(空间)的角度来看:

    自动变量:其作用域仅仅局限于其定义的函数中,存储在动态存储区,注意存储在动态存储区的数据,在没有显式初始化的时候,其变量的值是随机的,无用的。

    静态变量:存储在静态存储区,静态变量包括静态全局变量和静态局部变量,静态变量在没有显式初始化的时候会被初始化为0或者null,并且只初始化一次(初始化不等同于赋值)。

    静态全局(外)变量:作用域只是在其定义的源文件中有效,对外部变量起到了“屏蔽”的作用。
    静态局部变量:其作用域是其定义的函数中。

    外部变量:把全局变量在其他源文件中声明成extern变量,可以扩展该全局变量的作用域至声明的那个文件,其本质作用就是对全局变量作用域的扩展。

    寄存器变量:存储在cpu的寄存器中,速度快,一般不需要程序员定义寄存器变量,这是由于一些编译器会把需要参加很多次计算的变量转化成寄存器变量,不允许程序员对寄存器变量的地址进行操作。

    2、从生存时间的角度来看:

    自动变量:随着函数的进栈和出栈而创建和销毁

    静态变量:长期存在静态存储区,直到程序结束

    外部变量:长期存在静态存储区,直到程序结束

    寄存器变量:离开函数值就会消失

    static

    静态存储类型定义变量在未对其初始化时会对其初始化为默认值,其中int 型的默认初始化是0,其他选项的存储类型不能保证。

    修饰符类型

    C++ 允许在 char、int 和 double 数据类型前放置修饰符。

    修饰符是用于改变变量类型的行为的关键字,它更能满足各种情境的需求。

    下面列出了数据类型修饰符:

    • signed:表示变量可以存储负数。对于整型变量来说,signed 可以省略,因为整型变量默认为有符号类型。
    • unsigned:表示变量不能存储负数。对于整型变量来说,unsigned 可以将变量范围扩大一倍。
    • short:表示变量的范围比 int 更小。short int 可以缩写为 short。
    • long:表示变量的范围比 int 更大。long int 可以缩写为 long。
    • long long:表示变量的范围比 long 更大。C++11 中新增的数据类型修饰符。
    • float:表示单精度浮点数。
    • double:表示双精度浮点数。
    • bool:表示布尔类型,只有 true 和 false 两个值。
    • char:表示字符类型。
    • wchar_t:表示宽字符类型,可以存储 Unicode 字符。

    修饰符 signed、unsigned、long 和 short 可应用于整型,signed 和 unsigned 可应用于字符型,long 可应用于双精度型。

    这些修饰符也可以组合使用,修饰符 signed 和 unsigned 也可以作为 long 或 short 修饰符的前缀。例如:unsigned long int

    C++ 允许使用速记符号来声明无符号短整数无符号长整数。您可以不写 int,只写单词 unsigned、short 或 longint 是隐含的。

    常量

    整数常量

    整数常量可以是十进制、八进制或十六进制的常量。前缀指定基数:0x 或 0X 表示十六进制,0 表示八进制,不带前缀则默认表示十进制。

    整数常量也可以带一个后缀,后缀是 U 和 L 的组合,U 表示无符号整数(unsigned),L 表示长整数(long)。后缀可以是大写,也可以是小写,U 和 L 的顺序任意。

    下面列举几个整数常量的实例:

    212         /* 合法的 */
    215u        /* 合法的 */
    0xFeeL      /* 合法的 */
    078         /* 非法的:8 不是八进制的数字 */
    032UU       /* 非法的:不能重复后缀 */

    以下是各种类型的整数常量的实例:

    85         /* 十进制 */
    0213       /* 八进制 */
    0x4b       /* 十六进制 */
    30         /* 整数 */
    30u        /* 无符号整数 */
    30l        /* 长整数 */
    30ul       /* 无符号长整数 */

    整数常量可以带有一个后缀表示数据类型,例如:

    实例

    int&nbsp;myInt&nbsp;=&nbsp;10;
    long&nbsp;myLong&nbsp;=&nbsp;100000L;
    unsigned&nbsp;int&nbsp;myUnsignedInt&nbsp;=&nbsp;10U;  

    浮点常量

    浮点常量由整数部分、小数点、小数部分和指数部分组成。您可以使用小数形式或者指数形式来表示浮点常量。

    当使用小数形式表示时,必须包含整数部分、小数部分,或同时包含两者。当使用指数形式表示时, 必须包含小数点、指数,或同时包含两者。带符号的指数是用 e 或 E 引入的。

    下面列举几个浮点常量的实例:

    3.14159       /* 合法的 */
    314159E-5L    /* 合法的 */
    510E          /* 非法的:不完整的指数 */
    210f          /* 非法的:没有小数或指数 */
    .e55          /* 非法的:缺少整数或分数 */

    浮点数常量可以带有一个后缀表示数据类型,例如:

    实例

    float&nbsp;myFloat&nbsp;=&nbsp;3.14f;
    double&nbsp;myDouble&nbsp;=&nbsp;3.14159;  

    字符常量

    字符常量是括在单引号中,例如,’x’ 可以存储在 char 类型的简单变量中。

    字符常量可以是一个普通的字符(例如 ‘x’)、一个转义序列(例如 ‘\t’),或一个通用的字符(例如 ‘\u02C0’)。

    在 C 中,有一些特定的字符,当它们前面有反斜杠时,它们就具有特殊的含义,被用来表示如换行符(\n)或制表符(\t)等。下表列出了一些这样的转义序列码:

    转义序列含义
    \|\ 字符
    \’‘ 字符
    \”” 字符
    \?? 字符
    \a警报铃声
    \b退格键
    \f换页符
    \n换行符
    \r回车
    \t水平制表符
    \v垂直制表符
    \ooo一到三位的八进制数
    \xhh . . .一个或多个数字的十六进制数

    下面的实例显示了一些转义序列字符:

    实例

    #include <stdio.h>
    int main()
    {
        printf("Hello\tWorld\n\n");
        return 0;
    }

    当上面的代码被编译和执行时,它会产生下列结果:

    Hello World

    字符常量的 ASCII 值可以通过强制类型转换转换为整数值。

    实例

    char&nbsp;myChar&nbsp;=&nbsp;'a';
    int&nbsp;myAsciiValue&nbsp;=&nbsp;(int)&nbsp;myChar;&nbsp;// 将 myChar 转换为 ASCII 值 97  

    字符串常量

    字符串字面值或常量是括在双引号 ” ” 中的。一个字符串包含类似于字符常量的字符:普通的字符、转义序列和通用的字符。

    您可以使用空格做分隔符,把一个很长的字符串常量进行分行。

    下面的实例显示了一些字符串常量。下面这三种形式所显示的字符串是相同的。

    "hello, dear"
    
    "hello, \
    
    dear"
    
    "hello, " "d" "ear"

    字符串常量在内存中以 null 终止符 \0 结尾。例如:

    char myString[] = "Hello, world!"; //系统对字符串常量自动加一个 '\0'
  • 全自动安装 typecho

    今天突然想做一个自助建站系统,就是可以让用户简单填写一个表单输入站点信息,然后自动创建一个网站,搞了半天踩了很多坑终于实现了一个小demo

    这次是用的typecho做实验,使用bash脚本编写,当然搞了半天主要不是脚本的问题,而是环境的问题,先来看看脚本,是比较简单的。

    #!/bin/bash
    
    # 设置变量
    domain="test.oi.ink"  # 替换为你的域名
    nginx_conf="/etc/nginx/sites-available/${domain}"  # Nginx 配置文件路径
    nginx_link="/etc/nginx/sites-enabled/${domain}"  # Nginx 软链接路径
    webroot="/var/www/${domain}"  # 网站根目录
    typecho_file="typecho.zip"  # Typecho 压缩包文件名
    
    
    # 创建网站目录
    echo "正在创建网站目录..."
    sudo mkdir -p "${webroot}"
    
    # 解压 Typecho 到网站目录
    echo "正在解压 Typecho..."
    sudo unzip -d "${webroot}" "${typecho_file}"
    # 修改文件权限
    echo "正在设置文件权限..."
    sudo chown -R www-data:www-data "${webroot}"
    sudo chmod -R 755 "${webroot}"
    
    # 创建 Nginx 配置文件
    echo "正在创建 Nginx 配置文件..."
    sudo bash -c "cat > ${nginx_conf}" <<EOL
    server {
        listen 80;
        server_name ${domain};
        root ${webroot};
        index index.php index.html index.htm;
    
        location / {
            try_files \$uri \$uri/ /index.php\$is_args\$args;
        }
    
        location ~ \.php$ {
            include snippets/fastcgi-php.conf;
            fastcgi_pass unix:/run/php/php8.1-fpm.sock;  # 根据你的 PHP 版本进行调整
        }
    }
    EOL
    
    # 创建 Nginx 软链接
    echo "正在创建 Nginx 软链接..."
    sudo ln -s "${nginx_conf}" "${nginx_link}"
    
    # 重启 Nginx
    echo "正在重启 Nginx..."
    sudo service nginx restart
    
    # 创建数据库
    dbname="test"
    dbuser="test"
    dbpass="asdasdasdg"
    mysql -u root -p -e "CREATE DATABASE $dbname"
    
    # 创建用户并授权
    mysql -u root -p -e "CREATE USER '$dbuser'@'localhost' IDENTIFIED BY '$dbpass'"
    mysql -u root -p -e "GRANT ALL PRIVILEGES ON $dbname.* TO '$dbuser'@'localhost'"
    
    echo "数据库 '$dbname' 已成功创建,并授权用户 '$dbuser' 可访问。"
    
    echo "完成!Typecho 站点已成功创建。"
    
    echo "数据库信息:"
    echo "数据库名:$dbname"
    echo "数据库用户:$dbuser"
    echo "数据库密码:$dbpass"

    然后我是在本地的WSL子系统中测试的,在配置环境时出现了很多坑

    首先是数据库,安装了N次MySql都不可以,每次都会有ERROR 2002 (HY000): Can‘t connect to local MySQL server through socket ‘/tmp/mysql.sock‘ (2)的错误提示,在网上找了很多资料和解决方案全试了还是有问题,应该是WSL的锅,最后换了Mariadb,一次完美装上,丝滑。

    然后是PHP的坑,最后莫名其妙的好了,配置Nginx的站点的时候整合PHP,每次网站都无法访问,最后怎么好了的我也没搞明白,配置文件总感觉没有问题~~~

    然后!下面就是脚本执行成功后正常打开啦网站,这里对typecho的安装文件进行一些修改,打开数据库配置那步的页面时会自动获取本次创建的数据库来继续安装。

  • Moments 教程之Docker部署

    Docker部署是我们最推荐的部署方式,Moments最新版本都会第一时间在dockerhub中发布,当您的机器安装Docker后一句话既可部署本项目。

    本篇教程包含:Docker安装Moment部署反向代理域名绑定

    您可以根据需要跳过期中的某些步骤!点击可快速跳转

    Docker安装

    如果您使用了宝塔面板可直接在导航中选择 docker 页面根据提示自动安装即可

    关于其他系统的安装方式(官方文档):

    1.Centos:Install Docker Engine on CentOS | Docker Documentation

    2.Debian:Install Docker Engine on Debian | Docker Documentation

    3.Ubuntu:Install Docker Engine on Ubuntu | Docker Documentation

    Moment部署

    当您的设备中已经安装并且启动Docker后,在终端执行以下内容:

    docker run -d \
    --name moments \
    -p 3000:80 \
    -v moments:/app \
    drizzle2001/moments

    -p 3000是指本机端口,可以修改为其他任意不冲突的端口号

    运行成功后,服务器开放3000端口即可访问 http://ip:3000

    关于时区,可以在容器内执行,切换时区为东八区,重启容器即可。

    ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

    反向代理

    为您的朋友圈绑定一个域名,推荐使用 nginx

    如果您是宝塔用户可以直接创建一个静态网站绑定域名,然后设置反向代理

    其他反向代理,Nginx配置参考:Nginx配置反向代理,一篇搞定! – 知乎 (zhihu.com)

    其他问题欢迎留言,可以提供免费代搭建支持

  • 朋友圈:界面优化

    这是新的Moments,界面仿照了微信朋友圈~

    欢迎体验:https://moments.shiyu.dev/

    另外友链方面也进行一些优化,之前是通过iframe嵌套显示友链,现在改成了API接口的形式,体验更佳。

    开源地址:Drizzle365/Moments (github.com)