黑马程序员——零基础学习iOS开发——05 C语言:进制、内存储存细节、类型说明符、位运算、数组

 

------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------


一、进制

1.什么是进制?

进制是一种计数的表现方式。

我们日常生活中用十进制,即每满10个,前一位加1,当前位清零;

而计算机只能读懂0和1,所以计算机是二进制,每满2个,前一位加1,当前位清零;

内存地址用十六进制计数,每满16个,前一位加1,当前位清零,十六进制中数字10到15用ABCDEF这六个字母表示;

当然,还有八进制,如果你愿意,三进制、四进制、五进制你都可以用,鉴于大多数人都不用这些进制,我猜它们可能不太好用,这里就不研究了。


我们来感受一下二进制、八进制、十六进制是如何使用的,数一下下面有多少个笑脸,分别用各种进制计数。

 微笑 微笑 微笑 微笑 

 微笑 微笑 微笑 微笑

 微笑 微笑 微笑 微笑

十进制:12   

二进制:1100

八进制:14

十六进制:C


2.printf 进制输出格式

 %d、%i  十进制形式输出整数
 %f 输出小数
 %o 八进制形式输出整数
 %x 十六进制形式输出整数
 %c   输出字符
 %p  输出地址

 
 //以16进制整数的形式输出一个数值
  printf("%x\n", number);   

3.定义进制格式

    // 默认情况下,就是十进制
    int number = 12;
    
    // 二进制(0b或者0B开头)
    int number2 = 0B1100;
    
    // 八进制(0开头
    int number3 = 014;
    
    // 十六进制(0x或者0X开头
    int number4 = 0xc;

试着判断一下下面的数字写法是否合理?

00011   0x0011 0x7H4  10.98  0986  .089  -109 

+178    0b325  0b0010   0xFFdc  96f  96.0f   96.0F

-.003    15.4e6  10e8.7  7.6e-6

上面标黄的写法是不合理的。


.089的写法是正确的 .089 == 0.089

+178的写法是正确的 +178 == 178

-.003的写法是正确的 -.003 == -0.003

15.4e6的写法是正确的 15.4e6 == 15.4 * 10的6次方

7.6e-6的写法是正确的 7.6e-6 == 7.6 * 10的-6次方



二、内存储存细节

1.字节和地址 

不同类型的数据占用的字节是不一样的,一个整型变量占用4字节(Byte),1字节(Byte)=8比特位(bit)

2.变量的存储

1>所占用字节数跟类型有关,也跟编译器环境有关



2>变量实例


//一个int类型数据占用4个字节,即32bit
//0000 0000 0000 0000 0000 0000 0000 1100
int num1 = 12;
//0000 0000 0000 0000 0000 0000 0000 1101
int num2 = 13; 

为变量分配内存地址时是从大往小分配的,下图中得内存地址是我虚构的,但不影响举例。

首先,由于num1和num2都是int类型,所以他们都占用4个字节。


  • 内存由大到小寻址
  • 只存储二进制形式
  • 每个变量都有地址:第一个字节的地址就是变量的地址

3>查看内存地址的两种方式:%x 和 %p

3.负数在内存中的存储

1>最高位(比特位)为1,表示负数,最高位为0,表示正数

2>负数的表示形式:反码、补码

4.取值范围

假设我们现在有1个字节的空间,这个空间内存储的数的取值范围是多少?

1字节等于8比特,1个比特储存的数有两种可能,0和1,8比特就有2的8次方种可能。

内存中第一位默认标识数组的正负,所以就只剩下2的7次方种可能。

所以1字节能存的数的取值范围为 负2的七次方~正2的七次方-1。


 5.练习题

写出下列变量在内存中的存储情况

int a = 134;

int b = 0;

int c = -10;

三、类型说明符

1.short和long

short和long可以提供不同长度的整数型数据


short == short int  

long == long int

下面是各种类型的占位符格式:

int         %d

short     %d

long      %ld

long long   %lld

char      %c

float / double  %f    %.nf(显示小数点后n位)

2.signed和unsigned

signed: 内存比特位中最高位作为符号位(0正1负)     取值范围  -2^31~2^31-

unsigned: 内存比特位中最高位不作为符号位    取值范围 0~2^32

unsigned输出   %u

unsigned long  %lu

四、位运算

1. 按位与 &  

1)功能:只有对应的两个二进制位均为1时,结果位才为1,否则为0。

2)举例:
            计算  9 & 5;
           9转换成二进制为: 1001
           5转换成二进制为: 0101
每一位按位与后的结果: 0001
0001转换为的十进制结果为:       1

3)规律: 二进制中,与1相&就保持原位,与0相&就为0。


2. 按位或 | 

1) 功能 
只要对应的二个二进位至少有一个为1时,结果位就为1,两个二进制位都不为1,结果位就为0。 
2)举例:
比如 9 | 5,其实就是1001 | 101 = 1101,因此 9 | 5 = 13 
       计算 9 | 5
       9转换成二进制  1001
       5转换成二进制  0101
  每一位按位与或的结果  1101
1101转换为的十进制结果为:13

3)任何数和0按位或,结果还是它本身

3. 按位异或 ^

1) 功能 
当对应的二进位相异(不相同)时,结果为1,否则为0。 
2)举例:
     比如9^5,其实就是1001^101=1100,因此9^5=12 
       计算 9 ^ 5;
       9转换成二进制  1001
       5转换成二进制  0101
每一位按位与或的结果  1100
1100转换为的十进制结果为:12
3)规律 
1>相同整数相^的结果是0。比如5^5=0 
2>任何数值跟0进行异或,结果还是原来的数值 9 ^ 0 == 9
3>多个整数相^的结果跟顺序无关。比如5^6^7=5^7^6 
4.>因此得出结论:a^b^a = b 


4. 按位取反 ~

1) 功能 
对整数a的各二进位进行取反,符号位也取反(0变1,1变0) 
2)举例:
     比如9按位取反  ~9
       计算   ~9 ;
       9转换成二进制  0000 0000 0000 0000 0000 0000 0000 1001
    按位取反后的结果  1111 1111 1111 1111 1111 1111 1111 0110
                        ~9 == -10 
             想了解负数的内存占用,需要复习原码、反码、补码


5. 左移 <<

1) 功能
a<<n, 把整数a的各二进位全部左移n位,高位丢弃,低位补0。左移n位其实就是乘以2的n次方 
2)举例:
            计算   9<<2 ;
        这一行0用来做参照物  0000 0000 0000 0000 

 0000 0000 0000 0000 
  
9转换成二进制后左移两位   00  00 0000 0000 0000 0000 0000 0000 1001  00
   丢弃前两位,后两位补0,每次左移相当于每个位的1想高位移动,相当于每左移一次,数字变为原来的二倍。所以9<<2==36 
               
3)规律:
1.由于左移是丢弃最高位,0补最低位,所以符号位也会被丢弃,左移出来的结果值可能会改变正负性
2.当计算a*2的n次方时,可以用a<<n来代替,左移的运算效率比乘法高。


6. 右移 >> 

1) 功能
把整数a的各二进位全部右移n位,保持符号位不变。右移n位其实就是除以2的n次方 
2) 举例
            计算   8>>2 ;
        这一行0用来做参照物  0000 0000 0000 0000   0000 0000 0000 0000 
      8符号位不动,右移两位   0000 00 0000 0000 0000 0000 0000 0000 1000
            最高位补的值与符号位保持一致,所以补两次0,最后结果为2 
3) 规律
1>为正数时, 符号位为0,最高位补0 
2>为负数时,符号位为1,最高位是补0或是补1 取决于编译系统的规定 
3>当计算a/2的n次方时,可以用a>>n来代替,右移的运算效率比除法高。


7. 练习题 

1> 在不用引入其他变量的情况下,使用位异或^运算符实现两个变量值的互换 
#include <stdio.h>
int main()
{
    int a = 7, b = 8;
    a = a^b;
    b = a^b;    // 相当于b= a^b^b == a
    a = a^b;    // 相当于 a = a^b^a== b
    
    printf("a=%d, b=%d\n", a, b);
    
    return 0;
}




2> 使用位与&运算符变量的奇偶性 
#include <stdio.h>
int main()
{
    int a = -4;
    int b = a & 1; // 取出a的二进制的最后一位
    if(b ==1)
        printf("a是奇数\n");
    else
        printf("a是偶数\n");
    
    return 0;
}




3> 编写一个函数,用来输出整数在内存中的二进制形式 

#include <stdio.h>
void printBinary(int number);
int main()
{
    printBinary(9);
    return 0;
}
void printBinary(int number)
{
    //记录现在挪到第几位
    int temp = 31;  //4个字节*8再减去1
    while(temp>=0)
    {
        int value = number>>temp & 1; //右移,依次取出每一位的值
        printf("%d", value);
        
        if (temp%4==0) // 每四位输出一个空格
        printf(" ");
        
        temp-- ;
    }
}



五、char类型 

1. 存储细节

我们都知道,计算机中只能储存0和1,那么计算机是如何将字符转换成0和1储存并输出出来的呢?
计算机遵照ASCII码表来将各种字符转换成对应的数组存入内存,ASCII码表来判断数字所代表的字符。
char类型包括字母(大小写)、数字、符号、以及系统特殊符号(比如回车)

2. 常见错误 

// 下面4句的写法都是错误的
char a = A;
char b = "A";
char c = 'ABCD';
char d = '男';

// 正确的写法:
char a = 'A';
char b = 'z';
char c = '+';

3.当做整型使用




4.%c和%d(%i)的使用

输出字符对应的ASC码数值   printf(“%d”, ‘A’);

输出ASC码数值对应的字符   printf(“%c”, 68);

5.转义字符

系统默认单引号、双引号为字符和字符串的标志,当我们想要输出单引号、双引号或其他符号时,就要用到转义字符。

转义字符

意义

ASCII码值

\n

将当前位置移到下一行开头(回车换行)

10

\t

跳到下一个TAB位置

9

\\

代表一个反斜线字符

92

\'

代表一个单引号字符

39

\"

代表一个双引号字符

34

\0

空字符

0


六、数组

一个 int 类型的变量能保存一个人的年龄,如果想保存整个班的年龄呢?

1. 什么是数组  

数组,从字面上看,就是一组数据的意思,没错,数组就是用来存储一组数据的 

数组的特点:
1)只能存放一种类型的数据 ,比如 int 类型的数组、 float 类型的数组
2) 里面存放的数据称为“元素”

2、数组的定义

1>定义   
1)声明数组类型、名称
2)声明数组的元素个数


2>数组的定义格式:int ages[5];
        

3.数组的简单使用

  • 简单初始化:int ages[5] = {19, 19, 20, 21, 25};
  • 元素有顺序之分,每个元素都有一个唯一的下标(索引),0开始
  • 数组元素的访问:ages[i]

4.数组的初始化 

初始化方式 
  • int a[3] = {10, 9, 6};
  • int a[3] = {10,9};
  • int a[] = {11, 7, 6};
  • int a[4] = { [1]=11, [0] = 7 };
常见错误 
  • int a[];
  • int[4] a;
  • int a[b];
  • a = {10, 11};
  • a[4] = {10,9,8,5}; 

5. 数组的内存分析 

  • 数组存储空间的大小  元素大小*元素个数    int size = sizeof(array);
  • 存储空间的划分(内存的分配是从高地址到低地址进行的,但一个数组内部的元素排序是从低到高进行的
  • 数组名的作用,查看元素地址  &ages[ ]相当于ages
  • 数组越界的注意

6. 数组的其他使用

1)数组名即代表数组地址:ages相当于&ages
2)数组作为函数参数,可以省略元素个数:void( array[ ] ) {    }
3)数组作为函数参数,传递的是整个数组的内存地址。修改函数形参数组元素的值,会影响到外面实参函数数组元素的值
4)求数组的长度(数组的元素个数):int length = sizeof(ages)/sizeof(int);
5)数组作为函数参数传递时,会被当做指针变量来使用,指针变量在64位编译器环境下,默认占8个字节。
       可以通过多定义一个形参作为数组长度


6)遍历数组

#include  <stdio.h>
void printArray(int ages[])   //定义函数
{
    for(int i=0; i<5; i++)
    {
        printf("ages[%d]的值是%d\n", i, ages[i]);
    }
}
int main()
{
    int ages1[5] = {1,2,3,4,5};
    printArray(ages1);        //调用函数,将数组ages1[]的地址传给形参ages[]
    return 0;
}


7)设计一个函数,找出数组元素的最大值
#include  <stdio.h>
int maxOfArray(int array[], int length)
{
    int max = array[0];
    for(int i=1; i<length; i++)
    {
        if (array[i] > max) max = array[i];
    }
    return max;
}
int main()
{
    int ages[] = {11, 90, 67, 100, 78, 60, 70, 89, 120};
    //引用函数,并将数组实参、数组长度实参传入函数maxOfArray
    int max = maxOfArray(ages, sizeof(ages)/sizeof(int));
    printf("%d\n", max);
    return 0;
}

六、二维数组

1.  什么是二维数组

一个数组能表示一个班人的年龄,如果想表示很多班呢?

什么是二维数组?int ages[3][10]; 三个班,每个班10个人,相当于3行10列,相当于装着3个一维数组

二维数组是一个特殊的一维数组它的元素是一维数组

例如int a[2][3]可以看作由一维数组a[0]和一维数组a[1]组成,这两个一维数组都包含了3个int类型的元素

2.  存储

存储大小 int ages[3][10];    ages[3][10]占用储存空间是 3*10*4=120字节

存储结构和顺序:二维数组内部的每个一维数组都是按从前往后的顺序排列地址的,一维数组内部个元素也是从前往后

存储地址问题:二维数组的地址是它首个一维数组的首元素的地址。 

3.  初始化

int a[3][4] = {1,2,3,4,5,6};

int a[3][4] = {{1,2,3,4},{1,2,3,4},{1,2,3,4}};

数组元素简单访问

int a[][5] = {3,21,31,2,32,1};

注意错误:

int a[3][4];

a[3] = {}; // 不能对二维数组中的某一个一维数组整体赋值,只能给每个元素单独赋值。

4.    遍历

遍历所有的元素:在一维数组遍历基础上嵌套一个for循环。

遍历地址:%p


小结:

1.注意不同进制的书写格式

    二进制 0b  八进制十六进制 0x

2.假如有n个字节的内存,可储存数字的取值范围就是:

    -2(n-1)次方 ~ +2(n-1)次方-1

2.数组内部元素的地址分配是正序的,从前向后的。

3.二维数组中的一维数组排序也是正序的。

4.相位异或:两个二进制位  相异=1,相同=0

    a^b^a == b^0 == b

5.左移、右移n位,相当于与乘以或除以2n次方

    左移、右移的计算效率比乘方高。

6.字符可以当做整型来使用(ASC码的值)0~128

7.数组作为函数参数时,相当于指针。会影响外面实参的值。




To be continue……




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值