C语言——类型、运算符、与表达式

November 13, 2018 · 再学C · 14次阅读

变量名是由字母和数字组成的序列,但其中第一个字符必须为字母,其中下划线“_”被看做字母,通常用于命名较长的变量名,以提高其可读性。
库例程的名字通常是下划线开头,所以变量名最好不要以下划线开头。大写小写字母是有区别的,它们是不同的名字。通常,在C语言中,变量名使用小写字母,符号常量名全部使用大写字母。
if等关键字必须小写。


数据类型及长度
C语言中提供了下列几种基本数据类型:

名称类型说明
char字符型占用一个字节,可以存放本地字符集中的一个字符
int整型通常反映了所用机器中整数的最自然长度
float单精度浮点型 
double双精度浮点型 

此外,还可以在这些基本数据类型的前面加上一些限定符。short与long两个限定符用于限定整型:
short int sh;
long int counter;
在上述这种类型的声明中,关键字int可以省略。

  • short类型通常为16位
  • long类型通常为32位
  • unsigned char类型变量取值范围为0~255(char对象占8位)
  • signed char类型的取值范围则为-128~127(char对象占8位)
  • long double类型表示高精度的浮点数。

同整型一样,浮点型的长度也取决于具体的实现,float、double与long double类型可以表示相同的长度,也可以表示两种或三种不同的长度。
Q:编写一个程序以确定分别由signed及unsigned限定的char、short、int与long类型变量的取值范围。采用打印标准头文件中的相应值以及直接计算两种方式实现。后一种方法的实现较困难一些,因为要确定各种浮点类型的取值范围。
A:默认的定义文件在C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include\,比如limits.h。用的是VS 2015版本,直接打开文件可以看到定义,下面贴一下:

//
// limits.h
//
//      Copyright (c) Microsoft Corporation. All rights reserved.
//
// The C Standard Library <limits.h> header.
//
#pragma once
#define _INC_LIMITS

#include <vcruntime.h>

_CRT_BEGIN_C_HEADER



#define CHAR_BIT      8         // number of bits in a char
#define SCHAR_MIN   (-128)      // minimum signed char value
#define SCHAR_MAX     127       // maximum signed char value
#define UCHAR_MAX     0xff      // maximum unsigned char value

#ifndef _CHAR_UNSIGNED
    #define CHAR_MIN    SCHAR_MIN   // mimimum char value
    #define CHAR_MAX    SCHAR_MAX   // maximum char value
#else
    #define CHAR_MIN    0
    #define CHAR_MAX    UCHAR_MAX
#endif

#define MB_LEN_MAX    5             // max. # bytes in multibyte char
#define SHRT_MIN    (-32768)        // minimum (signed) short value
#define SHRT_MAX      32767         // maximum (signed) short value
#define USHRT_MAX     0xffff        // maximum unsigned short value
#define INT_MIN     (-2147483647 - 1) // minimum (signed) int value
#define INT_MAX       2147483647    // maximum (signed) int value
#define UINT_MAX      0xffffffff    // maximum unsigned int value
#define LONG_MIN    (-2147483647L - 1) // minimum (signed) long value
#define LONG_MAX      2147483647L   // maximum (signed) long value
#define ULONG_MAX     0xffffffffUL  // maximum unsigned long value
#define LLONG_MAX     9223372036854775807i64       // maximum signed long long int value
#define LLONG_MIN   (-9223372036854775807i64 - 1)  // minimum signed long long int value
#define ULLONG_MAX    0xffffffffffffffffui64       // maximum unsigned long long int value

#define _I8_MIN     (-127i8 - 1)    // minimum signed 8 bit value
#define _I8_MAX       127i8         // maximum signed 8 bit value
#define _UI8_MAX      0xffui8       // maximum unsigned 8 bit value

#define _I16_MIN    (-32767i16 - 1) // minimum signed 16 bit value
#define _I16_MAX      32767i16      // maximum signed 16 bit value
#define _UI16_MAX     0xffffui16    // maximum unsigned 16 bit value

#define _I32_MIN    (-2147483647i32 - 1) // minimum signed 32 bit value
#define _I32_MAX      2147483647i32 // maximum signed 32 bit value
#define _UI32_MAX     0xffffffffui32 // maximum unsigned 32 bit value

// minimum signed 64 bit value
#define _I64_MIN    (-9223372036854775807i64 - 1)
// maximum signed 64 bit value
#define _I64_MAX      9223372036854775807i64
// maximum unsigned 64 bit value
#define _UI64_MAX     0xffffffffffffffffui64

#if _INTEGRAL_MAX_BITS >= 128
    // minimum signed 128 bit value
    #define _I128_MIN   (-170141183460469231731687303715884105727i128 - 1)
    // maximum signed 128 bit value
    #define _I128_MAX     170141183460469231731687303715884105727i128
    // maximum unsigned 128 bit value
    #define _UI128_MAX    0xffffffffffffffffffffffffffffffffui128
#endif

#ifndef SIZE_MAX
    #ifdef _WIN64
        #define SIZE_MAX _UI64_MAX
    #else
        #define SIZE_MAX UINT_MAX
    #endif
#endif

#if __STDC_WANT_SECURE_LIB__
    #ifndef RSIZE_MAX
        #define RSIZE_MAX (SIZE_MAX >> 1)
    #endif
#endif



_CRT_END_C_HEADER

根据:https://junchu25.wordpress.com/2008/02/29/the-c-programming-language-2-1/

先说关于符号和无符号的概念,有符号类型(signed),在二进制表示中,最高位为符号位。比如5(以short为例),二进制表现为0000000000000101,它的最高位是0代表正数,如果是1则代表负数。对于无符号类型,它的高位没有符号标记,所以可以空出一位来表示大一倍的正数。假设int类型可以表示2的31次方,则unsigned int可以表示2的32次方。 2^31 * 2所以是大一倍。
那么对于无符号数,直接给0取反并赋予对应的无符号类型,则可以得到这种无符号类型的最大值,而0均为无符号类型的最小值。
对于有符号数,同上,增加右移1位的操作,然后强制转换为对应的有符号类型即可得到最大值,最小值为负的最大值,直接取负即可。那么代码如下(基本属于copy,注意最后是个无符号类型的应当用%u输出,%d无法输出无符号整数,打印会有问题):
#include <stdio.h>

/*determine ranges of types*/
void main()
{
    /* signed ranges of types*/
    printf(" signed char min = %d \n", -(char)((unsigned char) ~0 >> 1));
    printf(" signed char max = %d \n", (char)((unsigned char) ~0 >> 1));
    printf(" signed short min = %d \n", -(short)((unsigned short) ~0 >> 1));
    printf(" signed short max = %d \n", (short)((unsigned short) ~0 >> 1));
    printf(" signed int min = %d \n", -(int)((unsigned int) ~0 >> 1));
    printf(" signed int max = %d \n", (int)((unsigned int) ~0 >> 1));
    printf(" signed long min = %d \n", -(long)((unsigned long) ~0 >> 1));
    printf(" signed long max = %d \n", (long)((unsigned long) ~0 >> 1));
    
    /*unsigned types*/
    printf(" unsigned char max = %u \n",(unsigned char) ~0);
    printf(" unsigned int max = %u \n",(unsigned int) ~0);
    printf(" unsigned short max = %u \n",(unsigned short) ~0);
    printf(" unsigned long max = %u \n",(unsigned long) ~0);
}

执行示例:

>cl demo.c
用于 x86 的 Microsoft (R) C/C++ 优化编译器 19.00.24215.1 版
版权所有(C) Microsoft Corporation。保留所有权利。

demo.c
Microsoft (R) Incremental Linker Version 14.00.24215.1
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:demo.exe
demo.obj

>demo.exe
 signed char min = -127
 signed char max = 127
 signed short min = -32767
 signed short max = 32767
 signed int min = -2147483647
 signed int max = 2147483647
 signed long min = -2147483647
 signed long max = 2147483647
 unsigned char max = 255
 unsigned int max = 4294967295
 unsigned short max = 65535
 unsigned long max = 4294967295

某些字符可以通过转义字符序列表示为字符和字符串常量。转义字符序列看起来像两个字符,但只表示一个字符。
另外我们可以用'\ooo'表示任意字节大小的位模式。其中ooo代表1~3个八进制数字(0-7)。这种位模式还可以用'\xhh'表示其中,hh是一个或多个十六进制数字(0-9,a-f,A-F)。因此,我们可以按照下列形式书写语句:

#define VTAB '\013'    /* ASCII纵向制表符 */
#define BELL '\007'    /* ASCII响铃符 */

十六进制形式:

#define VTAB '\xb'    /* ASCII纵向制表符 */
#define BELL '\x7'    /* ASCII响铃符 */

ANSI C语言中的全部转移字符序列如下:

\a    响铃符
\b    回退符
\f    换页符
\n    换行符
\r    回车符
\t    横向制表符
\v    纵向制表符
\\    反斜杠
\?    问号
\'    单引号
\"    双引号
\ooo  八进制数
\xhh  十六进制数

字符常量'\0'表示值为0的字符,也就是空字符null。通常用'\0'的形式代替0,以强调某些表达式的字符属性,但其数字值为0。常量表达式是仅仅只包含常量的表达式。这种表达式在编译时求值,而不在运行时求值。它可以出现在常量可以出现的任何位置。
字符串连接可以多个"one" " two"这样挨着就行(感觉又学到新知识了),代码举例:

#include <stdio.h>

void main()
{
    printf("shizsfia" " zheshi0");
}

输出是shizsfia zheshi0
'x'"x"的区别:前者是一个整数(用其他语言多了就会感觉这有点坑),其值是字母x在机器字符集中对应的数值;后者是一个包含一个字符以及一个结束符'\0'的字符数组。
枚举

enum boolean {NO, YES}

在没有显式说明的情况下,enum类型中的第一个枚举名值为0,第二个为1,第三个为2...如果指定了部分枚举名的值,那么未指定的枚举名的值将依着最后一个指定值向后递增。
Q:在不使用运算符&&或||的条件下编写一个与下面的for循环语句等价的循环语句。

for (i = 0; i < lim -1; && (c = getchar()) != '\n' && c != EOF; ++i)
    s[i] = c;

A:代码如下,用的if

i = 0;
if (i < lim -1)
    if ((c = getchar()) != '\n')
        if (c != EOF;)
            ++i;

Q:编写函数htoi(s),把由16进制数字组成的字符串(包含可选的前缀0x或0X)转换为与之等价的整型值。字符串中允许包含的数字包括:0~9、a~f以及A~F。
A:一开始不明白,原来是转换十进制?。。。代码并非直接写的,只是添加了一点注释。

#include <stdio.h>

int htoi(char s[]);
main()
{
     char s1[] = "10";
     char s2[] = "2D";
     char s3[] = "3f";
     char s4[] = "0X4F";
     char s5[] = "0x3a";

     printf("%s -> %d\n", s1, htoi(s1));
     printf("%s -> %d\n", s2, htoi(s2));
     printf("%s -> %d\n", s3, htoi(s3));
     printf("%s -> %d\n", s4, htoi(s4));
     printf("%s -> %d\n", s5, htoi(s5));
}
int htoi(char s[])
{
     int n = 0;
     int i = -1;
    
     while (s[++i] != '\0') {
          if (i == 0 && s[i] == '0')//字符串以0开始,要么是0x 0X或者不必用于计算
               continue;
          else if (s[i] == 'x' || s[i] == 'X')//如果第二位是x 或者 X 那么后两位才是用于16进制转10进制计算所需要的
               continue;
          else if ('0'<= s[i] && s[i] <= '9')//这里就是字符到对应数大小的转换
               n = n * 16 + (s[i] - '0');
          else if ('a'<= s[i] && s[i] <= 'f')
               n = n * 16 + (s[i] - 'a' + 10);
          else if ('A' <= s[i] && s[i] <= 'F')
               n = n * 16 + (s[i] - 'A' + 10);
          else
               return -1;
     }
     return n;
}

Q:重新编写函数squeeze(s1, s2),将字符串s1中任何与字符串s2中字符匹配的字符都删除。
A:代码如下:

#include <stdio.h>

void squeeze(char s[], char t[]);
void main()
{
    char string_1[] = "ggqweewqeqweqweqweqeqweqweqweqhh";
    char string_2[] = "qwe";
    printf("sting_1 is %s.\nstring_2 is %s.\n", string_1, string_2);
    squeeze(string_1, string_2);
    printf("sting_1 is %s.\nstring_2 is %s.\n", string_1, string_2);
}
void squeeze(char string_1[], char string_2[])
{
    int i, j, k;
    for (k = 0; string_2[k] != '\0'; k++)
    {
        for (i = j = 0; string_1[i] != '\0'; i++)
            if (string_1[i] != string_2[k])
                string_1[j++] = string_1[i];
        string_1[j] = '\0';
    }
}

Q:重新编写函数any(s1, s2),将字符串s2中任一字符在字符串s1中第一次出现的位置作为结果返回。如果s1中不包含s2中的字符,则返回-1.(标准库函数strpbrk具有同样的功能,但它返回的是指向该位置的指针)
A:代码如下:

#include <stdio.h>

int any(char s1[], char s2[]);
void main()
{
    char string_1[] = "ggqweewqeqweqweqweqeqweqweqweqhh";
    char string_2[] = "okqwe";
    int res;
    printf("sting_1 is %s.\nstring_2 is %s.\n", string_1, string_2);
    res = any(string_1,string_2);
    printf("position is %d.\n", res);
}
int any(char s1[], char s2[])
{
    int i, j, posit = -1;
    for (i = 0; s2[i] != '\0'; i++)
    {
        for (j = 0; s1[j] != '\0'; j++)
        {
            if (s2[i] == s1[j])
            {
                posit = i;
                break;
            }
        }
        if (posit == -1)
            continue;
        else
            break;
    }
    return posit;
}

Q:编写一个函数setbits(x,p,n,y),该函数返回对x执行下列操作后的结果值:将x从第p位开始的n个(二进制)位设置为y中最右边n位的值,x的其余各位保持不变。
A:代码如下:

#include <stdio.h>

unsigned setbits(unsigned x, int p, int n, unsigned y);
void main()
{
    unsigned x = 0xa3, y = 0xb4, res;
    //x 163 10100011 y 180 10110100 --> 10101001 163
    int p = 1, n = 3;//p从右向左第0位开始
    res = setbits(x, p, n, y);
    printf("%u.\n", res);
}
unsigned setbits(unsigned x, int p, int n, unsigned y)
{
    unsigned temp;
    int i;
    i = p;
    temp = ~0 << n;//取反构造匹配p位开始n个的部分,与之相与使其置零
    while(i-- > 0){
        temp = temp << 1;
        temp++;
    }
    x = x & temp;
    temp = ~(~0 << n);
    y = y & temp;//将y移位到对应的位置
    while(p-- > 0){
        y = y << 1;
        y++;
    }
    
    return (x | y);//y与之相或,作用是覆盖对应的部分,即替换
}

Q:编写一个函数invert(x,p,n),该函数返回对x执行下列操作后的结果值:将x中从第p位开始的n个(二进制)位取反(即,1变成0,0变成1),x的其余各位保持不变。
A:代码如下:

#include <stdio.h>

unsigned invert(unsigned x, int p, int n);
void main()
{
    unsigned x = 0xa3, y = 0xb4, res;
    //x 163 10100011 y 180 10110100 --> 10101001 163
    // 10101101 --> 173
    int p = 1, n = 3;//p从右向左第0位开始
    res = invert(x, p, n);
    printf("%u.\n", res);
}

unsigned invert(unsigned x, int p, int n)
{
    unsigned temp;
    temp = ~0 << n;
    while(p-- > 0){
        temp = temp << 1;
        temp++;
    }
    return (~(~temp & x) & (~temp | x));
    //前半部分得到中间部分为取反结果,两侧是1
    //后半部分得到两侧是原结果,中间部分是1
    //两者相与两边则是原结果,中间则是取反结果
}

Q:编写一个函数rightrot(x,n),该函数返回将x循环右移(即从最右端移出的位将从最左端移入)n(二进制)位后所得到的值。
A:代码如下:

#include <stdio.h>

unsigned rightrot(unsigned x, int n);
void main()
{
    unsigned res, x = 0xa3;
    //10100011 --> 01100000000000000000000000010100 电脑上是32位
    int n = 3;
    res = rightrot(x, n);
    printf("%u.\n", res);
}

unsigned rightrot(unsigned x, int n)
{
    unsigned temp = ~0;
    unsigned temp_1 = ~0;
    while(temp != 0)
    {
        temp_1 = temp_1 << 1;
        temp = temp_1 << 1;
    }
    printf("temp_1 is %u.\n", temp_1);
    temp = ~(~0 >> 1);
    printf("temp is %u.\n", temp);
    
    while(n-- > 0)
    {
        temp = (~0 << 1) | x;//得到x最右一位的值 只有两种情况
        x = x >> 1;
        if (temp == ~0)//x最后一位为1
        {
            // x = ~(~0 >> 1) | x;
            x = temp_1 | x;
        }
        // else//x最后一位为0
        // {
            // //x最后一位为0 那么x右移已经自动补0了 这里就不用操作
        // }
    }
    return x;
}

Q:在求对二的补码时,表达式x&=(x - 1)可以删除x中最右边值为1的一个二进制位,请解释这样做的道理。用这一方法重写bitcount函数,以加快其执行速度。
A:x&=(x - 1)等效于 x = x & (x - 1),如果x最右边的一个二进制位为1,那么减1之后前面的位不变,最右这位变成0,这样与原本相“与”,那么x就变成了x-1,且最右一位是0,达到了删除最右边值为1的二进制位效果。更改代码如下(参考的),不过好像效率并没有提高?

#include <stdio.h>

int bitcount(unsigned x);

void main(void)
{
    unsigned x = 0xa3;
    int b = 0;
    
    b = bitcount(x);
    printf("b is %d.\n", b);
}

int bitcount(unsigned x)
{
    int b = 0;
    while(x != 0)
    {
        b++;
        x &= (x -1);
    }
    return b;
}

Q:重新编写将大写字母转换为小写字母的函数lower,并用条件表达式替换其中的if-else结构。
A:代码如下:

#include <stdio.h>

int bitcount(unsigned x);

void main(void)
{
    char string[] = "WGHigGYljuo";
    int n = 0;
    while(string[n++] != '\0')
        printf("%c", (string[n] >= 'A' && string[n] <= 'Z') ? (string[n] - 'A' + 'a') : string[n]);
    printf("\n");
}

标签:none

最后编辑于:2018/11/17 03:41

添加新评论