精华内容
下载资源
问答
  • 新版本还读者提供了计算机图形学领域的最前沿信息。《计算机图形学(OpenGL版)第3版》是计算机图形学课程的很好教材,也是计算机图形学专业人员的很好参考书。 编辑推荐 《计算机图形学(OpenGL版)第3版》配套的网站...
  • 数据的逻辑结构在计算机中的表示 C. 数据在计算机中的顺序存储方式 D. 存储在外存中的数据 (33) 设有下列二叉树:图见书P46 对此二叉树中序遍历的结果(B) A. ABCDEF B. DBEAFC C. ABDECF D. DEBFCA (34) 在面向...
  • 东莞市2011年信息学特长生测试题 工具软件##Description大家都知道任何数据、符号在计算机中都是以二进制的形式表示的。小明是某软件公司的工程师,公司应客户的需要,要开发一个小工具程序,用来实现任意进制之间的...

    东莞市2011年信息学特长生测试题 工具软件

    ##Description

    大家都知道任何数据、符号在计算机中都是以二进制的形式表示的。小明是某软件公司的工程师,公司应客户的需要,要开发一个小工具程序,用来实现任意进制之间的转换。可以将给定的m进制数x转化为n进制的P(所有数数据均能在长整型范围内处理)。现在也将这个任务交给你,请你来完成这个程序。

    Input

    从文件tool.in中读入数据,文件中只有一行,共有三个数x,m,n(2<=m,n<=10)。

    Output

    将结果输出到tool.out中,输出文件中只有一个数,那就是转换后的结果P。

    分析:先转成十进制再转n进制。。。

    代码

    #include <cstdio>
    using namespace std;
    
    int x,m,n;
    
    void change(int x)
    {
        if (x==0) return;
        change(x/n);
        printf("%d",x%n);
    }
    
    int change10(int x)
    {
        int o=1;
        int ans;
        while (x>0)
        {
            ans+=(x%10)*o;
            o*=m;
            x/=10;
        }
        return ans;
    }
    
    int main()
    {
        scanf("%d%d%d",&x,&m,&n);
        if (m!=10) x=change10(x);
        change(x);
    }
    展开全文
  • 第1课 进制转换

    2020-01-05 21:34:00
    但是,任何信息在计算机中都是采用二进制编码表示的,有时还会用到十六进制。 十进制计数原理采用“0” ~ “9”十个符号,运算规则“逢十进一”,基数是十。 二进制计数原理采用“0” 和“1”两个符号,运算规则是...

    学习目标

    1、理解二进制计数原理。
    2、掌握不同进制数之间的转换原理和实现方法。
    3、学会使用进制转换的原理解决一些实际问题。

    知识讲解

    实际生活中,人们使用十进制计数。但是,任何信息在计算机中都是采用二进制编码表示的,有时还会用到十六进制。
    十进制计数原理采用“0” ~ “9”十个符号,运算规则为“逢十进一”,基数是十。
    二进制计数原理采用“0” 和“1”两个符号,运算规则是“逢二进一”,基数是二。
    显然,十进制中的数“10”和二进制中的“10”、十六进制中的“10”是不一样的。为了区分,我们分别表示成(10)10、(10)2、(10)16。有时也会在一个数的后面加上英文字母D、B、H来分别表示该数是十进制数、二进制数或者十六进制数,如96D、110B、2B3FH等。

    进制转换的基本原理

    不同进制数之间转换的基本原理就是依据其“运算规则”和“权”的含义进行乘除运算。
    (1) 二进制数转换成十进制数
    一个二进制数转换成十进制数的方法是将其表示成“按权展开式”,再按十进制运算规则求和。这种方法可以扩展到任意 n 进制。
    (2) 二进制数与十六进制数之间的相互转换
    二进制数转换成十六进制数的方法是以小数点为准,往前、往后“四位一段”分别转换成十六进制数再求和,不满四位要补齐。
    (3) 十进制数转换成二进制
    十进制数转换成二进制要将整数和小数分开转换,最后再求和。整数的转换方法是:不断除以 2 求余数,最后反序输出;小数的转换方法是:不断乘以 2,将每次得到的整数部分依次输出,并且每次都将整数部分恢复为 0。

    进制转换的应用举例

    例1 进制转换

    【问题描述】
    将任意一个 n 进制整数 x 转换成十进制。
    【输入格式】
    第 1 行 1 个正整数 n,1<n<10;
    第 2 行 1 个整数 x。
    【输出格式】
    一行一个数,表示转换得到的十进制数,保证答案不超过 2147483647。
    【输入样例】
    2
    100110
    【输出样例】
    38
    【问题分析】
    读入 n 和 x,根据 n 和 x 的位数,分别求出 x 的每一位对应的“权值”,然后穷举每一位,将它乘以该位对应的权值,累加便可得到结果。
    更高效、更简洁的算法是采用“秦九韶公式”。对于样例输入,可以这样计算:
    (((((1*2)+0)*2+0)*2+1)*2+1)*2+0=38。具体实现采用“迭代法”,用一个变量不断乘以n,再加上下一位x[i]。

    #include<bits/stdc++.h>
    using namespace std;
    int main(){
        freopen(” change.in ” , ” r ” ,stdin);
        freopen( ” change.out ” , ” w ” ,stdout);
        int n,ans = 0,i = 0;
        char s[32];
        scanf(%d\n ” ,&n);
        while((s[i] = getchar()) != '\n'){  
              ans = ans * n + (s[i] - 48);  
              i++;
        } 
        printf(%d\n ” ,ans);
        return 0;
    }
    

    如果10<n<17,,则对于输入的“A” ~ “F"先处理成10 ~ 15 即可。如果要转换的是n进制的实数,可以把整数和小数部分截取出来分别进行转换,最后再合并再一起输出。

    例2 汽车牌照

    【问题描述】
    小 Y 最近发现街上的汽车越来越多了,作为汽车的重要标志——汽车牌照也是越来越不够用了,已经从以前的十进制发展到三十六进制了,以前的一个汽车牌照“苏 D88888”,现在的牌照“苏 D0YY11”。
    小 Y 突发其想,想知道他看到的大量汽车牌照中最近的两个汽车牌照相差多少?
    【输入格式】
    若干行(不超过 500000 行),每行为一个汽车牌照。
    每个汽车牌照为一个 7 位的字符串,格式为 SD×××××,其中一个 × 表示一个 0~9 或A~Z,所涉及的字母均为大写。
    【输出格式】
    一行一个数,表示最接近的两个汽车牌照之间的差值,要求为十进制数。
    【输入样例】
    SD12345
    SD88888
    SD22222
    SD99999
    【输出样例】
    1678245
    【问题分析】
    可以将所有汽车牌照(三十六进制数)转换成使十进制数,然后进行排序,再依次计算相邻两个数的差值,打擂台保留最小值。

    例3 数列(蓝桥杯)

    【问题描述】
    给定一个正整数 k,把所有 k 的方幂及所有有限个互不相等的 k 的方幂之和构成一个递增的序列。例如,当 k=3 时,这个序列是:1,3,4,9,10,12,13,…
    请求出这个序列的第 n 项的值(用十进制数表示)。
    【输入格式】
    一行两个正整数 k 和 n,之间用一个空格隔开,且 3≤k≤15,10≤n≤1000。
    【输出格式】
    一行一个正整数。
    【输入样例】
    3 100
    【输出样例】
    981
    【问题分析】
    该系列实际上就是:k0,k1,k0+k1,k2,k0+k2,k1+k2,k0+k1+k2,… 所以,只要将n转换成二进制数a[i],然后求出所有a[i]不等于0的对应项ki之和。

    #include<bits/stdc++.h>
    using namespace std;
    int a[10000];
    int main(){
    	int k,n,i;
    	cin>>k>>n;
    	i=0;
    	do{
    		a[++i]=n%2;
    		n/=2;
    	}while(n!=0);
    	int x=1;
    	int ans=a[1];
    	for(int j=2;j<=i;j++){
    		x*=k;
    		ans+=a[j]*x;
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    	
    

    实例巩固

    1.乘法口诀表

    输入一个自然数N(2<=N<=16),输出N进制乘法口诀表,请严格按照以格式输出:
    大写字母、右对齐,除了最左列外,每个数据项占4列。以下是N=16时的结果:
    * 0 1 2 3 4 5 6 7 8 9 A B C D E F
    0 0
    1 0 1
    2 0 2 4
    3 0 3 6 9
    4 0 4 8 C 10
    5 0 5 A F 14 19
    6 0 6 C 12 18 1E 24
    7 0 7 E 15 1C 23 2A 31
    8 0 8 10 18 20 28 30 38 40
    9 0 9 12 1B 24 2D 36 3F 48 51
    A 0 A 14 1E 28 32 3C 46 50 5A 64
    B 0 B 16 21 2C 37 42 4D 58 63 6E 79
    C 0 C 18 24 30 3C 48 54 60 6C 78 84 90
    D 0 D 1A 27 34 41 4E 5B 68 75 82 8F 9C A9
    E 0 E 1C 2A 38 46 54 62 70 7E 8C 9A A8 B6 C4
    F 0 F 1E 2D 3C 4B 5A 69 78 87 96 A5 B4 C3 D2 E1

    2.完全平方回文数

    【问题描述】
    给定一个十进制自然数的范围和进制的范围,十进制自然数范围在144700之间,是m*m形成的平方数,进制的范围在236之间。给定范围里的数中,有些数的平方,在某进制下既是完全平方数,又是回文数。本题的任务是统计给定范围内有多少个数的平方满足下列条件;仅在某一进制下既是完全平方数又是回文数。
    说明:32=9,因为它在十进制和十一进制中都是回文数,所以9不能算;同样,262=676也不算。
    【输入格式】
    一行四个整数,分别表示给定的十进制自然数m的范围和进制的范围。
    【输出格式】
    一行一个正整数,青示给定范围内满足条件的数的个数。
    【输入样例】
    1 100 9 11
    【输出样例】
    12
    【样例说明】
    6^2=36=33 base 11
    10^2=100=121 base 9
    11^2=121=121 base 10
    12^2=144=121 base 11
    20^2=400=484 base 9
    22^2=484=484 base 10
    24^2=576=484 base 11
    72^2=5184=3993 base 11
    82^2=6724=10201 base 9
    84^2=7056=5335 base 11
    91^2=8281=12321 base 9
    100^2=10000=1464 base 9

    3.罗马数字

    【问题描述】
    罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。
    字符 数值
    I 1
    V 5
    X 10
    L 50
    C 100
    D 500
    M 1000
    例如, 罗马数字 2 写做 II ,即为两个并列的 1。12 写做 XII ,即为 X + II 。 27 写做 XXVII, 即为 XX + V + II 。
    通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:
    I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
    X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。
    C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。
    给定一个整数,将其转为罗马数字。输入确保在 1 到 3999 的范围内。
    【输入样例1】
    3
    【输出样例1】
    III
    【输入样例2】
    9
    【输出样例2】
    IX
    【输入样例3】
    994
    【输出样例3】
    MCMXCIV

    4.八进制小数

    时光机

    展开全文
  • C语言中的指针(一)

    2017-05-27 18:42:47
    我们知道,任何一个数据在计算机中都是以二进制存储的,它们存储在计算机的内存地址中,而指针则是为了表示这些数据的地址数据。 介绍两种与指针有关的运算符:* 和 & * 称为间接运算符,是为了取得指针类型中的...

    指针简介

    指针:是一个值为内存地址的变量。指针类型的数据值为内存中的一个地址。
    我们知道,任何一个数据在计算机中都是以二进制存储的,它们存储在计算机的内存地址中,而指针则是为了表示这些数据的地址数据。
    介绍两种与指针有关的运算符:* 和 &
    * 称为间接运算符,是为了取得指针类型中的地址中的数据。& 称为取地址运算符,是为了取得数据的地址信息。
    声明指针类型: 数据类型 * 变量名。
    例:

    int x = 3;            //声明一个整型变量x;
    int *p;               //声明一个指向整形数据的指针变量p;
    p = &x;               //指针变量p指向整形数据x的地址;现在p的值存储的是x的地址。
    int ans = *p;         //取出指针p中地址中的数据;

    要创建指针变量,首先要声明指针变量的类型,他是指向什么类型的指针(就是说这个指针中存储的是那个数据类型的地址)。int *p 表示声明的指针类型存储的是int类型的数据的地址,float *p 表示声明的指针类型存储的是float类型的数据的地址……(以此类推)。 &为取地址符,&x表示取数据x的地址,p = &x 表示将int型数据变量存放的地址信息赋值给指针变量p; *p表示取出指针类型p中的地址中的数据(也就是x的值)。

    实例:

    #include<stdio.h>
    
    int main()
    {
        int x = 3;
        int *p;
        p = &x;
        int ans = *p;
        printf("存储数据x的地址信息为: %p\n\n", p);           //%p表示输出地址
        //存储数据x的地址信息就是指针变量p中存储的数据
        printf("指针变量p中地址对应的单元数据信息为: %d \n\n", ans);
    }

    运行截图:
    输出的数据地址信息会根据机器的不同有所不同

    声明指针:我们已将知道,声明指针首先要知道指针指向的数据类型,这是因为不同数据类型占用的存储空间不同,即使占用的存储空间相同,他们的存储方式也不尽相同,所以为了更准确的表示数据,我们需要声明指针指向的数据类型。int p中, 运算符表示声明了一个指针类型,int表示这个指针指向的数据类型是int类型。但是指针是什么类型呢?指针就是指针,它属于指针类型,不属于整形或浮点型等其他类型,一个指针类型一般占八个字节的存储空间。

    指针在函数之间的通信

    思考一个问题:
    有两个数据 x 和 y,我们现在要把输入的 x 和 y 交换数据后输出, 其中要求交换数据部分写在一个子函数中。

    我们知道,若这个题写在主函数中,会很简单,建立一个临时变量temp就大功告成了。

    temp = x;
    x = y;
    y = temp;

    但是若要写在子函数中怎么办,我们知道子函数只能返回一个数值,不可能同时返回 x 和 y,同时我们也不可能在子函数中直接修改主函数的数据值,所以若要在子函数中进行数据交换,该怎么告诉主函数呢?这时候指针就帮上了大忙。
    因为指针变量中存储的是数据的地址信息,对于一个数据来讲,他的地址信息是唯一的,所以,我们可以在子函数中直接对主函数中 x 和 y 的地址进行操作,这样就直接改变了 x 和 y的数据信息。

    #include<stdio.h>
    
    void swap(int *x, int *y);             //函数参数为指针类型的数据
    
    int main()
    {
        int x, y;
        scanf("%d%d", &x, &y);
    
        printf("交换数据之前的x = %d ,  y = %d\n\n", x, y);
    
        swap(&x, &y);                    //因为函数接收指针类型的数据,所以实参要传递xy的地址,而不是传递xy的值
    
        printf("交换数据之后的x = %d ,  y = %d\n\n", x, y);
    }
    
    void swap(int *x, int *y)
    {
        int *px = x;
        int *py = y;
    
        int temp;        //临时变量
    
        temp = *px;
        *px = *py;
        *py = temp;
    }

    运行截图:

    主函数不多说,看子函数,首先子函数的形参是指针类型的x和y,说明x和y中存储的是指针信息,传递实参的时候要用&取出数据x和y的地址传递,子函数中又定义了两个指针变量px, py(这里注意,他们是在子函数中定义的,所以在子函数作用域结束时他们会被销毁),使px = x, py = y,这样px和x指针就同时指向主函数中的数据x,py和y指针同时指向主函数中的数据y,我们又设置了一个整形临时变量temp,利用temp交换了指针px和py中的数据信息,*px就代表主函数中的x值, *py就代表主函数中的y值,我们这样就直接交换了程序变量在内存中的数据,因为数据的地址是唯一的,所以就间接改变了主函数中的x和y的值。

    那么问题来了,我可以交换地址中的数据,那我可不可以直接交换数据地址呢?
    ……我试了试,好像不行(c++中的引用变量应该是可以的,但可惜C语言没有引用变量),因为子函数的参数是两个地址,所以我们能够改变地址里的数据,但是无法交换两个地址(就好像如果我们不使用指针,也是无法将两个主函数中的数据直接在子函数中进行交换再返回给主函数的,因为子函数里交换的数据都是属于子函数内部作用域的数据,一旦离开子函数就会被销毁)。

    以上的例子只是为了解释指针在函数中信息传递的作用,不作为实际应用交换数据的程序(因为很蠢)。

    指针和数组

    我们应该清楚,指针和数组密不可分,我们常用的数组表示法实际上就是在变相的应用指针来访问数据。

    #include<stdio.h>
    
    int main()
    {
        int count = 0;
        int *p = NULL;
    
        int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };             
    
        int *p1 = arr;
        int *p2 = &arr[0];
    
        printf("p1指向的地址为: %p\n\n", p1);
        printf("p2指向的地址为: %p\n\n", p2);
    
        printf("利用数组检索数据:\n");
        for (count = 0; count < 10;count++)
        {
            printf("%d ", arr[count]);
        }
    
        printf("\n");
        printf("利用指针检索数据:\n");
        p = arr;
        for (count = 0; count < 10; count++)
        {
            printf("%d ", *p);
            p++;
        }
        printf("\n");
    
    }

    运行截图:

    通过运行结果我们可以看到,数组arr[]的数组名arr其实是一个地址,他可以赋值给指针变量p1,它代表的就是数组首地址(也就是数组首元素的地址),而数组首元素的地址同时为&arr[0],因此指针变量p1 == p2,从图中我们也可以看出,我们可以用数组来遍历数组元素,同时也可以用指针来遍历数组元素,为什么?因为数组中的数据元素存储是连续的,将指针变量p指向数组首地址后,指针每次加一,指针都向后移动一个存储单元(不是移动一个字节),这是为什么声明指针必须声明指针类型的原因。一开始p== &arr[0], 即p指向arr[0],当p++后,p就指向了arr[1],所以,数组的表示其实是借助了指针。arr[n]的意思是*(arr+n),找到数组首地址,然后移动n个存储单元后取出该地址里的内容,该内容就是arr[n]的值。 注意:不要混淆 *(arr+n)和 *arr+n, *(arr+n) 表示的是arr数组第三个元素的值,而 *arr+n表示的是arr数组的第一个元素值加2。

    函数、数组、指针

    假设要编写一个处理数组的函数,函数的作用求数组中所有元素的和,应该如何调用此函数?

    #include<stdio.h>
    
    int Sum(int *arr, int len);
    
    int main()
    {
        int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
    
        int ans = Sum(arr,10);
    
        printf("ans = %d\n", ans);
    }
    
    int Sum(int *arr, int len)
    {
        int count = 0;
        int ans = 0;
        for (count = 0; count < len; count++)
        {
            ans += arr[count];
            //或者是  ans += *(arr+count);
        }
    
        return ans;
    }

    首先我们来看子函数Sum,他的参数有两个,一个是指针变量arr,一个是整形变量len,因为该函数的作用是求数组中个元素的和,但是函数参数不能传递一个数组,但由于数组元素是按顺序存储的,所以传递了数组元素的首地址,这样便能通过地址的增加访问到整个数组元素。指针变量arr接收主函数数组的首地址,len接收主函数的数组长度(数据的数量)。
    除了传递首指针和数组长度来约束外,我们还可以用首尾指针来约束子函数处理数据的范围。

    #include<stdio.h>
    int main()
    {
        ...
        int ans = Sum(arr,arr+10);          //首指针+尾指针
        ...
    }
    
    int Sum(int *start, int *end)
    {
        int ans = 0;
    
        while (start < end)
        {
            ans += *start;
            start++;
        }
    
        return ans;
    }

    这里的end指针指向的是数组最后元素后面的一个地址,c语言保证再给数组分配空间时,指向数组后面一个位置的指针仍是有效的指针,但是不要访问该位置,因为该位置本身不属于数组。
    在c语言中,arr[i]和*(arr+i)是等价的,只不过由于指针表示法更接近于机器语言,因此用指针可以生成一些高效的代码。

    指针操作

    1、赋值:
    c语言中,我们可以把地址赋给指针变量,但是赋值时要注意地址应该和指针类型兼容,就是说,不要把double类型的数据赋值给int类型的指针。(例:int *p = &x)
    2、解引用:
    解引用便是求指针变量中存储的地址中的变量信息,用*标识符表示。(例:int x = *p)
    3、取址:
    和所有变量一样,指针变量也有自己的地址和值,对指针而言,&运算符给出指针本身的地址(不是指针指向的地址)。
    4、指针与整数相加:
    c语言中,我们可以用+运算符把指针于整数相加,此时整数会和指针所指向的类型的大小相乘,然后把结果与处地址相加。例如,对于int arr[]数组来讲,假设arr的地址是2000, 那么arr+2的地址不是2002,而是2008,这是为什么?因为int类型的数据在内存中存放占用四个字节,因此每次arr+1都是增加4个字节而不是一个字节。但是如果相加的结果超过了指针指向的数组范围,那么结果就是未定义的。
    5、递增指针:
    递增指针,自增,自增的长度也是根据指针类型来确定,int型增四个字节,double型增八个字节……
    6、指针减去一个整数:
    指针减去一个整数,相当于指针前移。举例:对于一个数组arr[10];现有指针变量p = &arr[4];那么p-2 = &arr[2],减去的长度依然视指针类型决定,int减四个字节,double减八个字节。
    7、递减指针:
    和递增指针一样。只不过是递减。
    8、比较:
    使用关系运算符可以比较指针的大小,前提是指针必须指向同类型的数据。

    关于指针的减法,指针减去一个整数等于一个指针,指针减去一个指针等于一个整数。所以我们要想求数组的长度,可以用尾指针减去首指针的方法来计算。
    PS:编译器不会帮我们检查指针越界的问题,不会帮我们检查指针是不是指向有效内容,所以我们在使用指针的时候一定要谨慎。
    野指针:没有初始化的指针。当我们定义一个指针时,一定要给他初始化,不然这个指针就会指向内存中任意一个单元,若你没发现并对这个指针里的数据内容进行了操作,幸运的话,啥事没有,不幸的话,系统崩溃。所以,初始化指针,很重要!若你不知道要怎样初始化指针,就把他赋值为空,一定不能让他变成野指针。

    int *p;                   //未初始化的指针(不要这样写)
    
    int *p = NULL;            //初始化为空的指针
    
    int x = 3;
    int *p = &x;              //初始化为指向x的指针

    const 指针:

    当我们使用const修饰指针时,有时候是为了保护数据不被修改。
    举例:如果需要求一个数组的全部元素的和,我们可以将数组的首地址传给子函数,在子函数中进行求和并返回,这时我们只需要读数据就可以完成任务,不需要对数据进行处理。但是因为我们传递的数据是主函数中数组的地址信息,所以子函数完全有权利去修改数据,这样的话,如果我们没有注意到子函数修改了数据,那么主函数中数组的数据就发生了我们不知道的改变,会影响我们程序之后的运行。那么如何保证我们传进去的数组不被修改呢?很简单,传入参数的时候把形参用const修饰就可以了。

    #include<stdio.h>
    
    int Sum(const int *arr, int len);          //求和函数
    
    int main()
    {
        int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
    
        int ans = Sum(arr, 10);
    
        printf("数组数据总和为: %d\n", ans);
    }
    
    int Sum(const int *arr, int len)
    {
        int count = 0;
        int ans = 0;
        for (count = 0; count < len; count++)
        {
            ans += arr[count];
        }
        return ans;
    }
    

    该程序中,主函数中的数组不是const类型,所以可以被修改,但是子函数中的参数是const类型,是不可被修改的,因此,在子函数中,我们是无法修改arr数组里面的元素的,但是跳出子函数,我们便又能对arr数组进行数据操作。所以,为了保证数据安全,如果我们编写的函数需要修改数组,那么我们就不用const,如果不需要修改数组,那我们最好加上const。

    在赋值方面,普通指针只能指向非const数组,而const指针可以指向const数组或非const数组。

    要注意,const的位置不同,指针表示的含义也不同。
    const int * p;代表指针类型p指向的数据不能被更改,但是指针变量p是可以再指向别的地方的,当他指向别的地方时,它指向的单元数据也就不能被修改。
    int * const p;代表指针类型p指向的地址是不能被修改的,但是他指向的内容是可以被修改的。
    const int * const p;代表指针类型p指向的地址是不能被修改的,同时指针指向的内容也是不能被修改的。

    展开全文
  • 非原创,参考链接: 位掩码(BitMask)的介绍与使用 一、前言 位运算在我们实际开发中用得很少,主要原因还是它对于我们而言不好读、不好懂、也不好计算...程序中的所有数在计算机内存中都是以二进制的形式储存的。位运算

    非原创,参考链接:
    位掩码(BitMask)的介绍与使用

    一、前言

    位运算在我们实际开发中用得很少,主要原因还是它对于我们而言不好读、不好懂、也不好计算,如果不经常实践,很容易就生疏了。但实际上,位运算是一种很好的运算思想,它的优点自然是计算快,代码更少。

    二、基本知识介绍

    二进制:

    二进制是由1和0两个数字组成的,它可以表示两种状态,即开和关。所有输入电脑的任何信息最终都要转化为二进制。目前通用的是ASCII码。最基本的单位为bit。

    位运算:

    程序中的所有数在计算机内存中都是以二进制的形式储存的。位运算说穿了,就是直接对整数在内存中的二进制位进行操作。比如,and运算本来是一个逻辑运算符,但整数与整数之间也可以进行and运算。举个例子,6的二进制是110,11的二进制是1011,那么6 and 11的结果就是2,它是二进制对应位进行逻辑运算的结果(0表示False,1表示True,空位都当0处理)。

    三、问题引用

    老鼠试毒
    有1000瓶水,其中有一瓶有毒,小白鼠只要尝一点带毒的水24小时后就会死亡,问至少要多少只小白鼠才能在24小时内鉴别出哪瓶水有毒?
    在这里插入图片描述
    这里,位掩码的使用就可以巧妙的解决此问题。

    我们先将问题简化一下:假设只有8瓶水,其中1瓶有毒。
    8杯水分别编号.png
    -----------------------------------------------------8杯水分别编号.png-----------------------
    将该矩阵转置,得:
    在这里插入图片描述
    --------------------------------------水杯矩阵转置.png---------------------------------------
    依上述场景,取4只容器,转置后的矩阵数列配组合溶液:
    取数位上为1的水,放入相应的容器,即:
    第一杯:只包含8号水
    第二杯:包含4、5、6、7号水
    第三杯:包含2、3、6、7号水
    第四杯:包含1、3、5、7号水

    取4只老鼠,编号1、2、3、4,分别喝下第一杯…第四杯水,
    4只老鼠的生死状态依次记为 w x y z,(w,x,y,z = {0,1})
    死亡记作1,非死亡记作0
    将二进制数列wxyz转为十进制,则得到有毒水的号码。
    假设6号水有毒,那么往回推算,不难看出,第2、3只老鼠会死亡,
    得到的wxyz的数列就是0110,转十进制后就是6。

    将1000瓶依次编号:1,2,3,4,…,1000; 且都记作二进制;
    那我们要用多少位来表示呢?
    总数是1000,2^9=512, 2^10=1024,于是至少要10位才够表示,
    也就是:0000000001,0000000010,0000000011,…,1111101000;
    道理同上。

    四、结合实际问题

    我们已经见识了二进制的厉害之处了,接下来我们结合代码来看看,在iOS开发中的应用(其实在任何开发中都一样)

    • 在实际开发中,我们常常遇到权限的判断的问题,比如说,不同的用户对系统有不同的操作权限,有的用户可能有多种权限,我们最常规的办法就是每一个权限定义一个BOOL值。
      假设,某系统有4种权限,那么,就有了:
    @interface BM_User : NSObject
    
    @property (nonatomic, assign) BOOL permission1;
    
    @property (nonatomic, assign) BOOL permission2;
    
    @property (nonatomic, assign) BOOL permission3;
    
    @property (nonatomic, assign) BOOL permission4;
    
    @end
    

    那用户A同时拥有permission1、permission2、permission4怎么表示呢?

        BM_User *userA = [[BM_User alloc] init];
        userA.permission1 = YES;
        userA.permission2 = YES;
        userA.permission4 = YES;
    

    这样的操作大家见多了吧?那我们来看看另一种写法:

    @interface BM_User : NSObject
    
    @property (nonatomic, assign) OptionPermission permission;
    
    @end
    

    有人就要问了,OptionPermission是什么鬼?来,继续。。。

    /**
     权限枚举
    
     - 1: permission1,二进制第1位,0表示否,1表示是
     - 2: permission2,二进制第2位,0表示否,1表示是
     - 4: permission3,二进制第3位,0表示否,1表示是
     - 8: permission4,二进制第4位,0表示否,1表示是
     */
    typedef NS_OPTIONS(NSUInteger, OptionPermission) {
        permission1 = 1 << 0,//0001,1
        permission2 = 1 << 1,//0010,2
        permission3 = 1 << 2,//0100,4
        permission4 = 1 << 3,//1000,8
    };
    

    那用户A同时拥有permission1、permission2、permission4怎么表示呢?

        BM_User *userA = [[BM_User alloc] init];
        userA.permission = permission1 | permission2 | permission4;
    

    是不是神清气爽?

    现在我们就具体化4种权限,并给出基础位掩码的表达及运算:

    #ifndef BM_Head_h
    #define BM_Head_h
    
    /**
     权限枚举
    
     - 1: 是否允许查询,二进制第1位,0表示否,1表示是
     - 2: 是否允许新增,二进制第2位,0表示否,1表示是
     - 4: 是否允许修改,二进制第3位,0表示否,1表示是
     - 8: 是否允许删除,二进制第4位,0表示否,1表示是
     */
    typedef NS_OPTIONS(NSUInteger, OptionPermission) {
        ALLOW_SELECT = 1 << 0,//0001,1
        ALLOW_INSERT = 1 << 1,//0010,2
        ALLOW_UPDATE = 1 << 2,//0100,4
        ALLOW_DELETE = 1 << 3,//1000,8
    };
    
    #endif /* BM_Head_h */
    
    #import "BM_Permission.h"
    #import "BM_Head.h"
    
    @interface BM_Permission ()
    
    /** 存储目前的权限状态 */
    @property (nonatomic, assign) OptionPermission flag;
    
    @end
    
    @implementation BM_Permission
    
    
    /** 重新设置权限 */
    - (void)setPermission:(OptionPermission)permission {
        self.flag = permission;
    }
    
    /** 添加一项或多项权限 */
    - (void)enable:(OptionPermission)permission {
        self.flag |= permission;
    }
    
    /** 删除一项或多项权限 */
    - (void)disable:(OptionPermission)permission {
        self.flag &= ~permission;
    }
    
    /** 是否拥某些权限 */
    - (BOOL)siAllow:(OptionPermission)permission {
        return (self.flag & permission) == permission;
    }
    
    /** 是否禁用了某些权限 */
    - (BOOL)isNotAllow:(OptionPermission)permission {
        return (self.flag & permission) == 0;
    }
    
    /** 是否仅仅拥有某些权限 */
    - (BOOL)isOnlyAllow:(OptionPermission)permission {
        return self.flag == permission;
    }
    

    五、写在最后

    • 大家还可以自行搜索一下NS_OPTIONS与NS_ENUM的区别,他们都是用来定义枚举的,但其用法是有很大不同。

    NS_ENUM定义通用枚举,NS_OPTIONS定义位移枚举
    参考链接:详解枚举NS_OPTIONS与NS_ENUM的区别与格式

    • 博主我最近一直在考虑优化代码,正在开发的项目中就有很多权限判断的问题,我也在寻找各种各样更好的写法。
    • 也希望大家重视代码的表达,因此更加优化自己的代码。
    • 补充
      a&~b: 清除标志位b;
      a|b: 添加标志位b;
      a&b: 取出标志位b;
      a^b: 取出a与b的不同部分;
    展开全文
  • ”命令,在出现“组策略”窗口中依次选择“在计算机配置→管理模板→系 统”,双击“关闭自动播放”,在“设置”选项卡中选“已启用”选项,最 后单击“确定”按钮即可。  13、如何恢复被破坏的系统引导文件 ...
  • 会计理论考试题

    2012-03-07 21:04:40
    5.声音与视频信息在计算机内是以 ___D___ 表示的。 A、模拟信息 B、模拟信息或数字信息 C、数字形式D、二进制形式的数字 6.在Windows98中,要恢复回收站中的文件,只要___B____。 A、双击该文件 B、用鼠标把该文件施...
  • C#微软培训教材(高清PDF)

    千次下载 热门讨论 2009-07-30 08:51:17
    超越今天各自营的超越今天各自营的 超越今天各自营的 Web 站点 站点站点 站点 把 把把 把 Internet 建成一 建成建成 建成 个 一个一 一个可 可个可 可 以互相交换组件的地方 以互相...
  • 4) 双方的计算机中都关闭了防火墙,或者防火墙策略中没有阻止网上邻居访问的策略。 Windows 98/2000/XP/2003访问XP的用户验证问题 首先关于启用Guest什么不能访问的问题: 1、默认情况下,XP 禁用Guest帐户 2...
  • C#微软培训资料

    2014-01-22 14:10:17
    超越今天各自营的超越今天各自营的 超越今天各自营的 Web 站点 站点站点 站点 把 把把 把 Internet 建成一 建成建成 建成 个 一个一 一个可 可个可 可 以互相交换组件的地方 以互相...
  • windows 程序设计

    2011-07-24 21:16:30
    连结程序使用该信息在.EXE文件中建立一个表格,在加载程序时,Windows使用它将呼叫转换Windows函数。 WINDOWS程序设计选项 说明Windows程序设计的多种技术,本书提供了许多范例程序。这些程序使用C语言撰写并...
  • DBX260中文说明书

    2013-04-24 11:07:35
    这些接口260 DriveRackÔ提供了MIDI功能,输入输出和转接插孔可让260 DriveRackÔ接 MIDI链路中的任意处。 RS485控制母线输入(DB-9接口型) 该输入网络连接用来接收 DriveRackÔ网络链中其它单元送来的信息。 ...
  • 然而,分布式系统这个词在计算机系统中被如此广泛应用以至于它的使用变得有点贬值。许多这方面的混乱来源于缺乏对物理的分布和逻辑的分布的区分。通过区分这两个概念,就可以更准确地描述一个分布式系统的属性。对于...
  • 分布式系统设计

    热门讨论 2007-07-12 15:59:47
    然而,分布式系统这个词在计算机系统中被如此广泛应用以至于它的使用变得有点贬值。许多这方面的混乱来源于缺乏对物理的分布和逻辑的分布的区分。通过区分这两个概念,就可以更准确地描述一个分布式系统的属性。对于...
  • excel的使用

    2012-11-25 17:06:01
    编辑区中输入数据,然后按住Ctrl键,同时敲一下回车,所有选中的单元格中都出现了这一数据。(7) 单元格中显示公式如果工作表中的数据多数是由公式生成的,想要快速知道每个单元格中的公式形式,以便编辑修改...
  • assertion(断言)软件开发中是一种常用的调试方式,很多开发语言中都支持这种机制。实现中,assertion就是程序中的一条语句,它对一个boolean表达式进行检查,一个正确程序必须保证这个boolean表达式的值...
  • 入门学习Linux常用必会60个命令实例详解doc/txt

    千次下载 热门讨论 2011-06-09 00:08:45
    文件doc版,可自行转成txt,手机上看挺好的。 本资源来自网络,如有纰漏还请告知,如觉得还不错,请留言告知后来人,谢谢!!!!! 入门学习Linux常用必会60个命令实例详解 Linux必学的60个命令 Linux提供...
  • java 面试题 总结

    2009-09-16 08:45:34
    assertion(断言)软件开发中是一种常用的调试方式,很多开发语言中都支持这种机制。实现中,assertion就是程序中的一条语句,它对一个boolean表达式进行检查,一个正确程序必须保证这个boolean表达式的值...
  • 每个图 G = (V, E) 被表示为节点 V 和边 E 的集合,每个节点 i∈V 都可以和特征向量 x_i 相关联,每条边 (i, j) ∈ E 都可以和特征向量 x_ij 关联起来。这些特征可以表示节点类型、边的方向等。如果一个节点或者一条...
  • 数据的逻辑结构在计算机中的表示 C. 数据在计算机中的顺序存储方式 D. 存储在外存中的数据 (33) 设有下列二*树: 对此二*树中序遍历的结果______。(B) A. ABCDEF B. DBEAFC C. ABDECF D. DEBFCA (34) 在面向...
  • 裘宗燕教授是北京大学数学学院信息科学系的,关心的主要学术领域包括计算机软件理论、程序设计方法学、程序设计语言和符号计算。已出版多部著作和译著,包括《程序设计语言基础》(译著,1990),《Mathematica数学...
  • 裘宗燕教授是北京大学数学学院信息科学系的,关心的主要学术领域包括计算机软件理论、程序设计方法学、程序设计语言和符号计算。已出版多部著作和译著,包括《程序设计语言基础》(译著,1990),《Mathematica数学...
  • C++程序设计语言(特别版)--源代码

    热门讨论 2012-04-23 07:33:51
    裘宗燕教授是北京大学数学学院信息科学系的,关心的主要学术领域包括计算机软件理论、程序设计方法学、程序设计语言和符号计算。已出版多部著作和译著,包括《程序设计语言基础》(译著,1990),《Mathematica数学...
  • 大学物理-卢德馨序言

    2010-03-19 20:25:56
    最基本的体现是天文学、地学、化学、生命科学中都包含着物理过程 或现象.这些学科中用到不少物理学概念和术语是很自然的.最基本还意味 着任何理论都不能和物理学的定律相抵触.例如,如果某种理论破坏能量守恒 ...
  • 操作系统(内存管理)

    热门讨论 2009-09-20 12:55:25
    不过,即使是这样一个简单的计算机中,您也会有问题,尤其是当您不知道程序的每个部分将需要多少内存时。如果您的空间有限,而内存需求是变化的,那么您需要一些方法来满足这些需求: 确定您是否有足够的内存来...
  • 计算机内部的信息可以分成控制信息和数据信息二大类;控制信息可分为指令和控制字两类;数据信息可分为数值信息和非数值信息两类。 1-8 什么叫二进制?使用二进制有何优点和缺点? 解: 二进制是基数2,每位的权...
  • 他的操作系统Ximu以及TCP/IP协议的实现他的书中都有介绍,并且应用到了商业产品中。 译者: 蒋慧 蒋慧,女,1973年2月出生。1995年毕业于南京通信工程学院计算机系,获计算机应用专业工学学土学位。1998年于南京...
  •  苏正泉,1995年毕业于解放军信息工程学院计算机及应用专业,高级工程师。IT项目管理、软件开发、系统管理和网络管理方面都有非常丰富的实践经验。曾发表过多篇计算机专业论 文,并翻译过多部计算机专业技术书籍...
  • 与我一同工作的同志对本研究所做的任何贡献均已论文中作了明确的说明并表示了谢意。 研究生签名: 日期: 东南大学学位论文使用 授权声明 东南大学、中国科学技术信息研究所、国家图书馆有权保留本人...

空空如也

空空如也

1 2 3
收藏数 41
精华内容 16
关键字:

任何信息在计算机中都表示为