C语言小知识

目录

C中未初始化的变量,默认值是多少?

C语言中定义int变量,默认值是多少?

C语言中有bool变量吗?

5 == 5.0? 

有符号数与无符号运算时数强制类型转换方式及底层表示

int 默认为有符号

优先级

mode argument w is used to truncate

printf函数的返回值是输出的长度

枚举类型

指针 

字符串赋值

strcat()函数:连接字符串 

strrev()函数:字符串逆置(倒序、逆序)

malloc函数返回的是void *类型

结构体

共用体union

sizeof(10.5) == 8

(x>>y) is equivalent to dividing x with 2^y

按位取反~

函数作用域

C语言内联函数

C 可变参数

goto


C中未初始化的变量,默认值是多少?

int, char, float, double

#include <stdio.h>

int main() {
    int a;
    char b;
    float c;
    double d;
    printf("%d ", a);
    printf("%d ", b);
    printf("%f ", c);
    printf("%lf ", d);  // 0 0 0.000000 0.000000
    return 0;
}

C语言中定义int变量,默认值是多少?

区分变量的类型,有两种情况:

1 局部变量。

局部变量在没有显示初始化时,其值C语言规范没做要求,可以是随机值,也可以是编译器随意给定的值(另一种说法:以前残留在堆栈里的随机值)比如gcc编译器的局部变量就是随机值,可能为任何值。而微软的编译器,如VC或VS,则会初始化全为c,即0xCCCCCC;

2 全局变量或静态局部变量。

所有的全局变量,即定义在函数外的变量,默认值为0。

所有的静态局部变量,即定义在函数内部的(eg:static int a)形式的,默认值为0。

C语言中有bool变量吗?

C语言中定义了

  • 6种基本数据类型:short,int,long,float,double,char
  • 4种构造类型:数组,结构体(struct),共用类型(union),枚举类型(enum)
  • 指针类型和空类型

C语言并非没有bool类型,而是在C99标准之前没有。C99标准中新增的头文件中引入了bool类型,与C++中的bool兼容。该头文件为stdbool.h

C++中定义了额外的两种基本数据类型:bool和wchar_t;

C语言中若需要使用bool类型可以借用int类型自己定义一下,例如:

typedef int bool;
#define TRUE 1
#define FALSE 0

然后就可以在程序中使用bool类型了。用32位的int类型来当bool使用,这样无疑浪费了内存资源,因此对于内存敏感的程序中可以使用char来定义bool类型:

typedef char bool;
#define TRUE 1
#define FALSE 0

5 == 5.0? 

对的

有符号数与无符号运算时数强制类型转换方式及底层表示

#include <stdio.h>

int main() {
    unsigned int i = 23;
    signed char c = -23;
    printf("i = %d, i = %u\n", i, i);  
    printf("c = %d, c = %u\n", c, c);
    if (i > c)
        printf("Yes\n");
    else if (i < c)
        printf("No\n");
    return 0;
}

当执行一个运算时(如这里的i>c),如果它的一个运算数是有符号的而另一个数是无符号的,那么C语言会隐式地将有符号 参数强制类型为无符号数,并假设这两个数都是非负的,来执行这个运算。

所以对应回上面的例子,就是它先把-23(变量c的值)这个有符号数强制转换成无符号数,然后再与23(变量i)的值,来进行比较,并假设这两个数原本都是非负的,然后进行比较。

要想这段代码正常执行,我们需要怎么办呢?很简单,把if语句改为if((int)i > c)即可。这样程序就会认为是两个有符号数在进行比较,-23就不会隐式地转换为无符号数

int 默认为有符号

优先级

结合方向只有三个是从右往左,其余都是从左往右。

所有双目运算符中只有赋值运算符的结合方向是从右往左。

另外两个从右往左结合的运算符也很好记,因为它们很特殊:一个是单目运算符,一个是三目运算符

C语言中有且只有一个三目运算符。

逗号运算符的优先级最低,从左到右计算,例如(5,3,0)结果为0,(0,3,5)结果为5

对于优先级:算术运算符 > 关系运算符 > 逻辑运算符 > 赋值运算符。逻辑运算符中“逻辑非 !”除外。

++自增运算符优先级高于取值运算符* 高于+

!(右到左)优先级高于&&高于||

mode argument w is used to truncate

printf函数的返回值是输出的长度

枚举类型

枚举类型的定义形式为:

enum typeName{ valueName1, valueName2, valueName3, ...... };

例如,列出一个星期有几天:

  1. enum week{ Mon, Tues, Wed, Thurs, Fri, Sat, Sun };

可以看到,我们仅仅给出了名字,却没有给出名字对应的值,这是因为枚举值默认从 0 开始,往后逐个加 1(递增);也就是说,week 中的 Mon、Tues ...... Sun 对应的值分别为 0、1 ...... 6。

#include <stdio.h>  //printf
#include <stdlib.h> //system
typedef enum {
    a = 15,
    b = 17,
    c = 19,
    d
} e;
int main(int argc, char const *argv[]) {
    e u = c;
    e v = d;
    printf("%d %d",u,v); // 19 20
    system("pause");
    return 0;
}

指针 

#include <stdio.h>

int main() {
    int arr[3] = {4,2,3};
    int *p = &arr[1];
    int *k = p++;    //相当于 int *k = p; p = p + 1;
    int r = p - k;
    printf("%d %d %d\n", p, k, r);    // p指向arr[2], k指向arr[1]
    printf("%d %d %d", arr, &arr[1], &arr[2]);
    return 0;
}
int *ptr[12]; // ptr is an array of 12 pointers to integers.

指针几个字节跟系统的寻址能力有关,譬如以前是16位地址,指针即为2个字节,现在一般是32位系统,所以是4个字节;64位,则就为8个字节。 

#include <stdio.h>

int main() {
    char* s = "a string";
    char *p = s;
    while(*p++) {
        printf("%s\n", p);
    }
    printf("%d\n", (int)(p-s));

    return 0;
}

字符串赋值

下面的方式不可以

char ch[20];

ch = "Study Tonight";

下面的方式可以

char ch[20] = "Study Tonight";

#include <stdio.h>

int main() {
    char *ptr = "Solo Learn";
    printf(ptr+5);    // 输出Learn
    return 0;
}
#include <stdio.h>

int main() {
    char c[5] = "foo";
    printf("%d", sizeof(c));  // 5
    return 0;
}
#include <stdio.h>
#include <string.h>

int main() {
    char s[20] = "Hello\0Hi";
    printf("%d %d", strlen(s), sizeof(s));  // 5 20
    return 0;
}

strcat()函数:连接字符串 

头文件:#include <string.h>

strcat() 函数用来连接字符串,其原型为:
    char *strcat(char *dest, const char *src);

【参数】dest 为目的字符串指针,src 为源字符串指针。

strcat() 会将参数 src 字符串复制到参数 dest 所指的字符串尾部;dest 最后的结束字符 NULL 会被覆盖掉,并在连接后的字符串的尾部再增加一个 NULL。

注意:dest 与 src 所指的内存空间不能重叠,且 dest 要有足够的空间来容纳要复制的字符串。

【返回值】返回dest 字符串起始地址 

strrev()函数:字符串逆置(倒序、逆序)

头文件:#include<string.h>

strrev()函数将字符串逆置,其原型为:
    char *strrev(char *str);

【参数说明】str为要逆置的字符串。

strrev()将str所指的字符串逆置。

【返回值】返回指向逆置后的字符串的指针。

strrev()不会生成新字符串,而是修改原有字符串。因此它只能逆置字符数组,而不能逆置字符串指针指向的字符串,因为字符串指针指向的是字符串常量,常量不能被修改。 

malloc函数返回的是void *类型

Can't increase the size of statically allocated array.

结构体

结构体字节对齐 

共用体union

结构体和共用体的区别在于:结构体的各个成员会占用不同的内存,互相之间没有影响;而共用体的所有成员占用同一段内存,修改一个成员会影响其余所有成员。

结构体占用的内存大于等于所有成员占用的内存的总和(成员之间可能会存在缝隙),共用体占用的内存等于最长的成员占用的内存。共用体使用了内存覆盖技术,同一时刻只能保存一个成员的值,如果对新的成员赋值,就会把原来成员的值覆盖掉。

#include <stdio.h>
union Data {
    int one;
    int two;
};
int main() {
    union Data t;
    t.two = 3;
    printf("%d %d\n", t.one, t.two);
    t.one = 5;
    printf("%d %d\n", t.one, t.two);
    return 0;
}

sizeof(10.5) == 8

(1)整形

整形数值 默认是int型,例如:22,33

如果要表示一个long型,则加 l、L后缀。例如:22L、33L

(2)浮点型

浮点数默认是双精度dobule型的。例如:33.3

如果要表示单精度浮点型,加f、F后缀。例如:33.3F

(3)字符串型

包含在双引号""中的字符序列,是字符串。例如:“hello,world”

(4)字符型

用单引号''包含,或者用/转义的字符,是字符型。例如:/n、'a'

(x>>y) is equivalent to dividing x with 2^y

按位取反~

#include <stdio.h>

int main() {
    int a = 2;
    printf("%d\n", -1 << 1);
    if (a == ~(-3)) {
        a = ~a + 2 << 1;    //~按位取反优先级高于+高于<<
        printf("%d", a);
    } else {
        a = ~a;
        printf("%d", a);
    }
    return 0;
}

解释:

-3的原码为10000011

-3的反码为11111100

-3的补码为11111101

按位取反补码即为00000010,因为符号位为0即表示正数,反码原码与补码一样也为00000010,也就是十进制的2

2的补码00000010,按位取反得到的补码为11111101,反码为11111100,原码为10000011,也就是十进制的-3

-1的原码为10000001

-1的反码为11111110

-1的补码为11111111

左移一位补码为11111110,反码即是11111101,原码即是10000010,也就是十进制的-2

例如,我们用8位二进制表示一个数,则+11的原码为00001011,-11的原码就是10001011

正数的反码和原码一样,负数的反码就是在原码的基础上符号位保持不变,其他位取反。

十进制原码反码
60000 01100000 0110
-31000 00111111 1100

补码是一种用二进制表示有符号数的方法。正数和 0 的补码就是该数字本身。负数的补码则是将其对应正数按位取反再加 1。

十进制原码反码补码
60000 01100000 01100000 0110
-31000 00111111 11001111 1101

有符号右移(>>)

该操作符会将第一个操作数向右移动指定的位数。向右被移出的位被丢弃,拷贝最左侧的位以填充左侧。由于新的最左侧的位总是和以前相同,符号位没有被改变。所以被称作 “符号传播”。

例如, 9 >> 2 得到 2:

     9 (base 10): 00000000000000000000000000001001 (base 2)
                  --------------------------------
9 >> 2 (base 10): 00000000000000000000000000000010 (base 2) = 2 (base 10)

相比之下, -9 >> 2 得到 -3,因为符号被保留了。

     -9 (base 10): 11111111111111111111111111110111 (base 2)
                   --------------------------------
-9 >> 2 (base 10): 11111111111111111111111111111101 (base 2) = -3 (base 10)

无符号右移(>>>)

该操作符会将第一个操作数向右移动指定的位数。向右被移出的位被丢弃,左侧用 0 填充。因为符号位变成了 0,所以结果总是非负的。

对于非负数,有符号右移和无符号右移总是返回相同的结果。例如 9 >>> 2 和 9 >> 2 一样返回 2:

      9 (base 10): 00000000000000000000000000001001 (base 2)
                   --------------------------------
9 >>> 2 (base 10): 00000000000000000000000000000010 (base 2) = 2 (base 10)

但是对于负数却不尽相同。 -9 >>> 2 产生 1073741821 这和 -9 >> 2 不同:

      -9 (base 10): 11111111111111111111111111110111 (base 2)
                    --------------------------------
-9 >>> 2 (base 10): 00111111111111111111111111111101 (base 2) = 1073741821 (base 10)
 -9 >> 2 (base 10): 11111111111111111111111111111101 (base 2) = -3 (base 10) 

① 负数的右移:需要保持数为负数,所以操作是对负数的二进制位左边补1。如果一直右移,最终会变成-1,即(-1)>>1是-1。

② 负数的左移:和整数左移一样,在负数的二进制位右边补0,一个数在左移的过程中会有正有负的情况,所以切记负数左移不会特殊处理符号位。如果一直左移,最终会变成0。
 

  • 算术运算,补码形式参与运算;读取时——原码形式读取;
  • 左右移运算,以补码形式参与运算,正数左右移、负数左移均以补 0 操作(符号位保持不变),负数右移以补 1 操作(符号位不变);
  • 按位运算,补码形式参与运算,符号位与数值位地位相同。

函数作用域

#include <stdio.h>

int i = 10;
void f(void) {
    int i = 5;
    i = 7;
}
void g() {
    i = 9;
}
int main() {
    i = 1;
    g();
    f();
    printf("%d", i);    // 9
    return 0;
}

#include <stdio.h>
#define square(x) x * x
int main() {
    printf("%d", square(4-1));  // 4 - 1 * 4 - 1 = -1
    return 0;
}
#include <stdio.h>
#define Square(x) (x*(x))
int main()
{
    int x = 5;
    printf("%d", Square(x+3)); // 5 +3 * (5 + 3)
}

macros only do textual replacement

(*Macros which tend to be used as a replacement for inline-functions.)

You can fix this in C++ using template functions which can handle all types and in C by specifying a concrete type (since even overloading isn't supported in C, the best you can get is different functions with suffixes):

// C
int SquareI(int x) { return x * x; }
float SquareF(float x) { return x * x; }
double SquareD(double x) { return x * x; }

// C++
template<typename T>
T Square(T x) { return x * x; }

Specifically for GCC, there is another solution, since GCC provides the typeof operator so we can introduce a temporary value within the macro:

#define Square(x) ({ typeof (x) _x = (x); _x * _x; })

Et voila: OGu08W - Online C Compiler & Debugging Tool - Ideone.com

C语言内联函数

C 可变参数

#include <stdio.h>

void dynamic(int s, ...) {
    printf("%d ", s);
}
int main() {
    dynamic((1,2,3,4));    //相当于dynamic(4);
    dynamic(5,6,7,8);
    return 0;
}
int func(int, ... ) 
{
    ;
}
 
int main()
{
   func(2, 2, 3);
   func(3, 2, 3, 4);
}

函数 func() 最后一个参数写成省略号,即三个点号(...),省略号之前的那个参数是 int,通常代表了要传递的可变参数的总数。

Variadic functions are functions that can take a variable number of arguments. In C programming, a variadic function adds flexibility to the program. It takes one fixed argument and then any number of arguments can be passed. The variadic function consists of at least one fixed variable and then an ellipsis(…) as the last parameter.

Values of the passed arguments can be accessed through the header file named as:

#include <stdarg.h>

<stdarg.h> includes the following methods:

Methods

Description

va_start(va_list ap, argN)This enables access to variadic function arguments.
va_arg(va_list ap, type)This one accesses the next variadic function argument.
va_copy(va_list dest, va_list src)This makes a copy of the variadic function arguments.
va_end(va_list ap)This ends the traversal of the variadic function arguments.

Here, va_list holds the information needed by va_startva_argva_end, and va_copy.

goto

#include <stdio.h>

int main() {
    int a = 1;
    if (a == 0)
        goto label;
    label: printf("%d ", a);
    printf("Yes");
    return 0;
}

output: 1 Yes

参考资料:

C语言中有bool变量吗?_xx18030637774的博客-CSDN博客

C语言中定义int变量,默认值是多少?Scanf(),Printf()浅析。_小元点点的博客-CSDN博客_c语言int默认值

C语言中有没有bool类型?_关注 Linux c/c++ 数据存储 网络 算法......-CSDN博客

C语言 字面量 - 寒魔影 - 博客园

c - #define Square(x) (x*(x)) - Stack Overflow

C语言运算符优先级和结合性一览表

C 可变参数 | 菜鸟教程

Variadic functions in C - GeeksforGeeks

有符号数与无符号数比较-详解_sanliangGoGoGo-CSDN博客_无符号和有符号数比较

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
学习C语言对于初学者来说,可以按照以下步骤来进行: 1. **理解基础知识**:开始之前,了解计算机基本概念如内存、数据类型、变量等。了解计算机是如何运作的以及C语言的基本结构是很重要的。 2. **选择教程或资源**:选择一本适合初学者的C语言教材,例如 "C Programming: A Modern Approach" 或者在线资源如C语言官方文档和Codecademy的教程。 3. **环境配置**:安装一个C编译器,比如GCC(GNU Compiler Collection),并学会使用文本编辑器(如Notepad++或Visual Studio Code)编写和运行C代码。 4. **语法入门**:从C语言的基础语法开始,比如变量声明、数据类型(如整型、浮点型、字符型)、运算符、控制结构(条件语句和循环)等。 5. **练习编写小程序**:通过编写简单的程序,如计算器、猜数字游戏等,来加深对语言的理解和实践。 6. **函数和数组**:学习函数的定义、调用以及数组的使用,这是C程序组织的关键。 7. **面向对象编程**:尽管C不是纯面向对象的语言,但理解指针和结构体,以及函数指针能让你接触到面向对象的元素。 8. **错误处理和输入输出**:学习如何处理程序中的错误,以及标准输入输出(如scanf和printf)的使用。 9. **阅读他人代码**:找一些开源项目来阅读,这有助于理解实际开发中C语言的应用。 10. **持续学习和实践**:编程是实践出真知,多做项目,不断调试和优化代码,逐渐提升编程能力。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值