精华内容
下载资源
问答
  • 11.下面赋值语句中正确的是()

    千次阅读 2019-05-19 23:53:12
    A 科学计数法,double类型赋值给double,正确。 B 浮点数默认类型为double,大范围不能赋值给小范围(除非强转),错误。 C double不能赋值给int(理由同b),错误。 D int虽可以转换成double,但在类型转换...
    A.double d=5.3e12;
    B.float f=11.1;
    C.int i=0.0;
    D.Double oD=3;

    答案选A

    A 科学计数法,double类型赋值给double,正确。

    B 浮点数默认类型为double,大范围不能赋值给小范围(除非强转),错误。

    C double不能赋值给int(理由同b),错误。

    D int虽可以转换成double,但在类型转换时无法进行自动装箱,故错误。

    展开全文
  • 完成以下描述赋值语句的LL(1)文法的递归下降分析程序 G[S]: S→V=E E→TE′ E′→ATE′|ε T→FT′ T′→MFT′|ε F→ (E)|i A→+|- M→*|/ V→i 2、[设计说明] 终结符号i为用户定义的简单变量,即...

    实验要求:

    1、[实验项目]

    完成以下描述赋值语句的LL(1)文法的递归下降分析程序

    G[S]:
    S→V=E
    E→TE′
    E′→ATE′|ε
    T→FT′
    T′→MFT′|ε
    F→ (E)|i
    A→+|-
    M→*|/
    V→i

    2、[设计说明]

    终结符号i为用户定义的简单变量,即标识符的定义。

    3、[设计要求]

    (1)输入串应是词法分析的输出二元式序列,即某算术表达式“专题1”的输出结果,输出为输入串是否为该文法定义的算术表达式的判断结果;

    (2)递归下降分析程序应能发现简单的语法错误;

    (3)设计两个测试用例(尽可能完备,正确和出错),并给出测试结果;

    (4)选做:如有可能,考虑如何用文法描述C语言的if语句,使整个文法仍然为LL1文法,并使得你的递归下降程序可以分析赋值语句和if语句。

    实验过程:

    1、文法扩充修改

    1.1、修改文法:

    (4)选做:如有可能,考虑如何用文法描述C语言的if语句,使整个文法仍然为LL(1)文法,并使得你的递归下降程序可以分析赋值语句和if语句。

    针对(4)选做我对文法进行了扩充和修改

    扩充修改后的文法:

    基本可以识别c语言if语句、判断语句和赋值语句:

    G[P]:
    P→S|Q|;
    S→V=E;
    V→i
    E→TR
    R→ATR|$
    T→FY
    Y→MFY|$
    F→CZ
    Z→OCZ|$
    C→BI
    I→XBI|$
    B→(E)|i
    A→+|-
    M→*|/
    X→a|o //a表示逻辑符号&&,o表示逻辑符号||
    O→t|d|g|l|u|e //t表示>=,d表示<=,g表示>,l表示<,e表示==,u表示!=
    Q→8JKH //8表示if在符号表中序号
    H→fJKH|9K|$ //f 表示 else if符号的组合、9表示else在符号表中的序号
    J→(E) //逻辑语句
    K→S|{U}|; //if语句程序体
    U→PU|{U}U|$

    1.2、扩充后E表示的表达式可以识别逻辑表达式

    规则:

    F→CF′
    F′→OCF′|ε
    C→BC′
    C′→XBC′|ε
    X→a|o//a表示逻辑符号&&,o表示逻辑符号||
    O→t|d|g|l//t表示>=,d表示<=,g表示>,l表示<

    将逻辑与(&&)和逻辑或(||)加入到表达式中,将关系运算符(<=、>=、==、!=、<、>)加入到表达式中。

    1.3、if语句文法设计说明

    Q→8JKH//8表示if在符号表中的序号
    H→89JKH|9K|ε//9表示else在符号表中的序号
    J→(E) //逻辑语句
    K→S|{U}|; //if语句程序体
    U→PU|{U}U|$

    这部分规则,表示的是if语句的识别规则:

    Q→if<判断语句><IF语句程序体><ELSE IF 或者 ELSE语句程序体>
    <ELSE IF 或者 ELSE语句程序体>→else if<判断语句><IF语句程序体><ELSE IF
    或者 ELSE语句程序体>|else<IF语句程序体>|$
    <判断语句>→(E) //逻辑语句
    <IF语句程序体>→S|{U}|; //if语句程序体
    U→PU|{U}U|$
    //U这个部分保证了if语句程序体中能够出现赋值语句和if语句的闭包,并且能识别普通的程序体

    3、

    其中 S→V=E; 规则我在最后加入了 ;
    终结符号,使所有的赋值表达式都比较符合c语言规则。

    2、递归下降分析程序设计说明

    2.1设计要求:

    (1)输入串应是词法分析的输出二元式序列,即某算术表达式“专题1”的输出结果,输出为输入串是否为该文法定义的算术表达式的判断结果;
    (2)递归下降分析程序应能发现简单的语法错误;
    (3)设计两个测试用例(尽可能完备,正确和出错),并给出测试结果;
    (4)选做:如有可能,考虑如何用文法描述C语言的if语句,使整个文法仍然为LL1文法,并使得你的递归下降程序可以分析赋值语句和if语句。

    2.2设计说明:

    (1)该语言大小写不敏感;

    (2)字母为a-zA-Z,数字为0-9;

    (3)对上述文法进行扩充和改造;

    (4)“/*……*/”为程序的注释部分;

    (5)“/*”作为左注释,“*/”作为右注释,分别作为一种类别,所以左注释和右注释必须成对出现,否则错误;左右注释中字符忽略不进行识别;

    (6)“//”作为单行注释,“//”后的字符忽略不进行识别。

    (7)扩充后E表示的表达式可以识别逻辑表达式

    规则:
    F→CF′
    F′→OCF′|ε
    C→BC′
    C′→XBC′|ε
    X→a|o//a表示逻辑符号&&,o表示逻辑符号||
    O→t|d|g|l//t表示>=,d表示<=,g表示>,l表示<

    将逻辑与(&&)和逻辑或(||)加入到表达式中,将关系运算符(<=、>=、==、!=、<、>)加入到表达式中。

    Q→8JKH//8表示if在符号表中的序号
    H→89JKH|9K|ε//9表示else在符号表中的序号
    J→(E) //逻辑语句
    K→S|{U}|; //if语句程序体
    U→PU|{U}U|$

    这部分规则,表示的是if语句的识别规则(将字母替换成文字意义的非终结符号):

    Q→if<判断语句><IF语句程序体><ELSE IF 或者 ELSE语句程序体>
    <ELSE IF 或者 ELSE语句程序体>→else if<判断语句><IF语句程序体><ELSE IF
    或者 ELSE语句程序体>|else<IF语句程序体>|$
    <判断语句>→(E) //逻辑语句
    <IF语句程序体>→S|{U}|; //if语句程序体
    U→PU|{U}U|$
    //U这个部分保证了if语句程序体中能够出现赋值语句和if语句的闭包,并且能识别普通的程序体

    3、程序功能描述

    (1)、能够录入一个.tys文件中的二元式内容;

    (2)、根据.tys文件内容进行语法分析,可识别

    ①赋值表达式
    ②逻辑表达式
    ③c语言if语句;

    (3)、根据输入的二元式内容进行分析语法分析,并打印结果;

    (4)、打印分析过程和错误提示。

    4、主要的数据结构描述

    4.1主要使用的java数据结构类型
    4.1.1 List
    list中添加,获取,删除元素
    List\<String\> person=new ArrayList\<\>();
    
    person.add("jackie"); //索引为0 //.add(e)
    
    person.add("peter"); //索引为1
    
    person.add("annie"); //索引为2
    
    person.add("martin"); //索引为3
    
    person.add("marry"); //索引为4
    
    person.remove(3); //.remove(index)
    
    person.remove("marry"); //.remove(Object o)
    
    String per="";
    
    per=person.get(1);
    
    System.out.println(per); .get(index)
    
    for (int i = 0; i \< person.size(); i++) {
    
    System.out.println(person.get(i)); //.get(index)
    
    }
    
    list中是否包含某个元素
    方法:.contains(Object o); 返回true或者false
    
    List\<String\> fruits=new ArrayList\<\>();
    
    fruits.add("苹果");
    
    fruits.add("香蕉");
    
    fruits.add("桃子");
    
    //for循环遍历list
    
    for (int i = 0; i \< fruits.size(); i++) {
    
    System.out.println(fruits.get(i));
    
    }
    
    String appleString="苹果";
    
    //true or false
    
    System.out.println("fruits中是否包含苹果:"+fruits.contains(appleString));
    
    if (fruits.contains(appleString)) {
    
    System.out.println("我喜欢吃苹果");
    
    }else {
    
    System.out.println("我不开心");
    
    }
    
    list中根据索引将元素数值改变(替换)
    .set(index, element); 和 .add(index, element); 的不同
    
    String a="白龙马", b="沙和尚", c="八戒", d="唐僧", e="悟空";
    
    List\<String\> people=new ArrayList\<\>();
    
    people.add(a);
    
    people.add(b);
    
    people.add(c);
    
    people.set(0, d); //.set(index, element);
    //将d唐僧放到list中索引为0的位置,替换a白龙马
    
    people.add(1, e); //.add(index, element);
    //将e悟空放到list中索引为1的位置,原来位置的b沙和尚后移一位
    
    //增强for循环遍历list
    
    for(String str:people){
    
    System.out.println(str);
    
    }
    
    list中查看(判断)元素的索引
      注意:.indexOf(); 和 lastIndexOf()的不同;
    
    List\<String\> names=new ArrayList\<\>();
    
    names.add("刘备"); //索引为0
    
    names.add("关羽"); //索引为1
    
    names.add("张飞"); //索引为2
    
    names.add("刘备"); //索引为3
    
    names.add("张飞"); //索引为4
    
    System.out.println(names.indexOf("刘备"));
    
    System.out.println(names.lastIndexOf("刘备"));
    
    System.out.println(names.indexOf("张飞"));
    
    System.out.println(names.lastIndexOf("张飞"));
    
    根据元素索引位置进行的判断
    if (names.indexOf("刘备")==0) {
    
    System.out.println("刘备在这里");
    
    }else if (names.lastIndexOf("刘备")==3) {
    
    System.out.println("刘备在那里");
    
    }else {
    
    System.out.println("刘备到底在哪里?");
    
    }
    
    利用list中索引位置重新生成一个新的list(截取集合)
    方法: .subList(fromIndex, toIndex);  .size() ; 该方法得到list中的元素数的和
    
    List\<String\> phone=new ArrayList\<\>();
    
    phone.add("三星"); //索引为0
    
    phone.add("苹果"); //索引为1
    
    phone.add("锤子"); //索引为2
    
    phone.add("华为"); //索引为3
    
    phone.add("小米"); //索引为4
    
    //原list进行遍历
    
    for(String pho:phone){
    
    System.out.println(pho);
    
    }
    
    //生成新list
    
    phone=phone.subList(1, 4); //.subList(fromIndex, toIndex)
    //利用索引1-4的对象重新生成一个list,但是不包含索引为4的元素,4-1=3
    
    for (int i = 0; i \< phone.size(); i++) { // phone.size()
    该方法得到list中的元素数的和
    
    System.out.println("新的list包含的元素是"+phone.get(i));
    
    }
    
    对比两个list中的所有元素
      //两个相等对象的equals方法一定为true,
    但两个hashcode相等的对象不一定是相等的对象
    
    //1.\<br\>if (person.equals(fruits)) {
    
    System.out.println("两个list中的所有元素相同");
    
    }else {
    
    System.out.println("两个list中的所有元素不一样");
    
    }
    
    //2.
    
    if (person.hashCode()==fruits.hashCode()) {
    
    System.out.println("我们相同");
    
    }else {
    
    System.out.println("我们不一样");
    
    }
    
    判断list是否为空
      //空则返回true,非空则返回false
    
    if (person.isEmpty()) {
    
    System.out.println("空的");
    
    }else {
    
    System.out.println("不是空的");
    
    }
    
    4.2 二元式文件结构

    二元式文件通过专题1的词法分析程序得到:

    其中一个测试用例为:

    (8,if)
    (3,()
    (3,()
    (3,()
    (1,num1)
    (24,>=)
    (1,num2)
    (3,))
    (41,&&)
    (3,()
    (1,num1)
    (14,+)
    (1,num2)
    (28,!=)
    (2,23)
    (3,))
    (3,))
    (42,||)
    (1,a)
    (24,>=)
    (1,num1)
    (3,))
    (3,{)
    (1,a)
    (23,=)
    (1,num1)
    (14,+)
    (1,num2)
    (3,?
    (8,if)
    (3,()
    (1,a)
    (24,>=)
    (1,num1)
    (3,))
    (3,{)
    (3,?
    (3,})
    (9,else)
    (3,{)
    (1,a)
    (23,=)
    (1,num1)
    (14,+)
    (1,num2)
    (3,?
    (3,})
    (1,a)
    (23,=)
    (1,num1)
    (14,+)
    (1,num2)
    (3,?
    (3,})
    (9,else)
    (8,if)
    (3,()
    (1,num1)
    (24,>=)
    (1,num2)
    (3,))
    (3,{)
    (8,if)
    (3,()
    (1,a)
    (24,>=)
    (1,num1)
    (3,))
    (3,{)
    (1,a)
    (23,=)
    (1,num1)
    (14,+)
    (1,num2)
    (3,?
    (3,})
    (9,else)
    (3,{)
    (1,a)
    (23,=)
    (1,num1)
    (14,+)
    (1,num2)
    (3,?
    (3,})
    (3,})
    (9,else)
    (8,if)
    (3,()
    (1,num1)
    (24,>=)
    (1,num2)
    (3,))
    (3,?
    (9,else)
    (3,{)
    (3,{)
    (8,if)
    (3,()
    (1,a)
    (24,>=)
    (1,num1)
    (3,))
    (1,a)
    (23,=)
    (1,num1)
    (14,+)
    (1,num2)
    (3,?
    (9,else)
    (3,{)
    (1,a)
    (23,=)
    (1,num1)
    (14,+)
    (1,num2)
    (3,?
    (3,})
    (3,})
    (3,})

    其源程序为:

    if(((num1>=num2)&&(num1+num2!=23))||a>=num1)
    { //该部分对if语句程序进行赋值语句和if语句的循环套嵌进行测试
    a = num1+num2;
    if(a>=num1)
    { ; } //该部分对边缘性{;}语句进行测试
    else
    {
    a = num1+num2;
    }
    a = num1+num2;
    }
    else if (num1>=num2)//该部分对else if 语句进行。
    {
    if(a>=num1)
    {
    a = num1+num2;
    }
    else
    {
    a = num1+num2;
    }
    }
    else if (num1>=num2)
    ; //该部分进行了边缘性测试只有 ‘ ;’ 的语句
    else
    {
    { //该部分进行了普通程序体测试,即{ }包括的程序语句。
    if(a>=num1)
    a = num1+num2;//该语句测试单赋值语句与if语句的嵌
    else
    {
    a = num1+num2;
    }
    }
    }
    可见该if语句包含了许多特殊、复杂和边缘的情况,非常具有代表性

    二元式文件内容被录入到
    private static List<String> InputStream = new ArrayList<String>(); //从二元式文件中拆解的符号穿输入流

    InputStream是一个Java
    List列表的一个对象,list列表是一系列的String类型的字符串,具体的操作:

    br = new BufferedReader(new InputStreamReader(new FileInputStream(fp.getName())));
    			String erYuanShi = "";
    			while((erYuanShi=br.readLine())!=null) {
    				//截取符号串
    				InputStream.add(erYuanShi.substring(erYuanShi.indexOf(",") + 1, erYuanShi.lastIndexOf(")")));
    			}
    			InputStream.add("#");  //末尾添加#号
    
    

    br
    为一个文件的读入流,通过使用br.readLine()方法读入二元式文件当前行内容并返回给String类型的变量erYuanShi,然后每一行的内容类似为(1,num1)的形式,但是我们需要就是num1,所以通过

    erYuanShi.substring(erYuanShi.indexOf(",")+ 1,erYuanShi.lastIndexOf(")"))

    方法将num1截取下来,放入List列表对象InputStream中,继续读文件,直到读取结束。这样就将二元式文件的内容读取到了List列表对象InputStream中。

    然后每次分析当前符号的时候,都需要到列表对象InputStream中读取一个字符串进行分析,但是读取列表内容的时候,不能直接使用,因为,源程序和我们的文法有转换的地方,也就if被8替换,a表示&&等等,所以读取list内容之后,要通过一个函数进行转换:

    public String get_indexP(int index)

    该函数要传入当前扫描指针位置,也就是indexP,根据indexP的位置,得到当前字符,然后该函数根据当前字符进行转换,返回转换后的字符。具体细节可以查看源程序或者该函数的批注。

    4.3 FIRST 集

    产生式 FIRST集

    产生式			FIRST集						
     *  P->S			[i]
     *  P->Q			[8]
     *  P->;			[;]
    	S->V=E;			[i]
    	V->i			[i]
    	E->TR			[(, i]
    	R->ATR			[+, -]
    	R->$			[$]
    	T->FY			[(, i]
    	Y->MFY			[*, /]
    	Y->$			[$]
    	F->CZ			[(, i]
    	Z->OCZ			[t, d, u, e, g, l]
    	Z->$			[$]
    	C->BI			[(, i]
    	I->XBI			[a, o]
    	I->$			[$]
    	B->(E)			[(]
    	B->i			[i]
    	A->-			[-]
    	A->+			[+]
    	M->*			[*]
    	M->/			[/]
    	X->a			[a]
    	X->o			[o]
    	O->l			[l]
    	O->e			[e]
    	O->g			[g]
    	O->t			[t]
    	O->u			[u]
    	O->d			[d]
    	
    	Q->8JKH			[8]
    	H->fJKH			[f]
    	H->9K			[9]
    	H->$			[$]
    	J->(E)			[(]
    	K->{U}			[{]
    	K->;			[;]
    	K->S			[i]
    	U->PU			[8, i, ;]
    	U->$			[$]
    	U->{U}U			[{]
    
    
    4.4 FOLLOW 集

    非终结符号 FOLLOW集

    非终结符号		FOLLOW集
    	P 			# 8 i ; { } 
    	Q 			# 8 i ; { } 
    	J 			i { ; 
    	K 			# f 8 9 i ; { } 
    	H 			# 8 i ; { } 
    	S 			# f 8 9 i ; { } 
    	V 			= 
    	E 			) ; 
    	R 			) ; 
    	T 			) + ; - 
    	Y 			) + ; - 
    	F 			) * + ; - / 
    	A 			( i 
    	M 			( i 
    	Z 			) * + ; - / 
    	C 			t d u e g ) * + ; l - / 
    	I 			t d u e g ) * + ; l - / 
    	B 			a d e g ) * + l - o / t u ; 
    	X 			( i 
    	O 			( i 
    	U 			} 
    
    
    	
    
    

    5、程序结构描述

    5.1 java类 及其方法分析
    5.1.1 Java 主类:递归下降语法分析的入口类
    public class RecursiveAnalysisMain {
    
    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
    		//1:构造递归下降语法分析类
    		RecursiveDescentParsing rdp = new RecursiveDescentParsing("zhuanti3_2.tys");
    		//2:执行算法
    		boolean k = rdp.grammaticalAnalysis();
    		if(k==true)
    			System.out.println("\n满足该文法");
    		else 
    			System.out.println("\n不满足该文法");
    	}
    
    }	
    
    

    该类只有一个main函数,该函数创建递归下降语法分析类的一个对象rdp,通过rdp对象调用语法分析入口函数grammaticalAnalysis()

    boolean k = rdp.grammaticalAnalysis();
    		if(k==true)
    			System.out.println("\n满足该文法");
    		else 
    			System.out.println("\n不满足该文法");
    
    

    grammaticalAnalysis()返回布尔类型,如果为真则表示分析成功,否则分析失败。

    5.1.2 Java 副类:递归下降分析类在这里插入图片描述

    1)私有变量:

    private BufferedReader br = null; //输入的二元式文件流,

    private BufferedReader br = null; //输入的二元式文件流,
    	private static List<String> InputStream = new ArrayList<String>(); //从二元式文件中读入内容,保存源代码。
    	private int indexP = 0; //扫描指针,初始为0
    	private int tab = 1; //制符表的数量,初始为1,作为源程序输出时的保持格式的制表符数量
    类方法分析:
    	//语法分析入口函数
    	public boolean grammaticalAnalysis() {
    		System.out.println("-------------分析过程-------------------");
    		System.out.print("    ");
    		return P();
    	}
    
    

    2)语法分析入口,打印提示信息后,直接进入非终结符号的P的分析函数:

    private boolean P() { 
    		String s =get_indexP( indexP);
    		//System.out.println(s);
    		
    		//System.out.println(sequals);
    		if(s.equals("i")) {
    			
    			if(!S())
    				return false;
    		}else if (s.equals("8"))
    		{
    			if(!Q())
    				return false;
    		}
    		else if (s.equals(";"))
    		{
    				print_indexp(indexP);indexP++;
    				return true;
    		}
    		else 
    			return false;
    		return true;
    	}
    
    

    String s =get_indexP( indexP); 通过
    get_indexP()函数得到扫描指针所在的二元式内容,并返回一个可以供各个非终结符号分析识别的符号。get_indexP()函数会将if转换为8返回,将elseif转换为f返回,也就根据文法的内容,get_indexP()函数进行转换,返回各类二元式代表的终结符号。

    如果当前的符号为i,也就是用户定义的变量(get_indexP()函数将所用的变量,标识符和数字全部转换为i,方便分析函数分析)就调用非终结符号S(赋值表达式的入口)的分析函数。

    如果当前的符号为8,也即if,会调用非终结符号的Q(if语句的入口)的分析函数。

    如果是终结符号 ‘;’ ,则扫描指针加1,返回真。

    3)非终结符号S(赋值表达式的入口)的分析函数

    private boolean S() { //S→V=E:    i    
    		String s =get_indexP( indexP);
    		print_indexp(indexP);
    		String sequals = get_indexP( ++indexP);;
    		//System.out.println(sequals);
    		if(s.equals("i")) {
    			if(sequals.equals("="))
    			{
    				print_indexp(indexP);indexP++;
    				if(!E())
    					return false;
    			}
    			else {
    				System.out.println("\n缺少 = ");
    				return false;
    			}
    			
    		}else
    		{
    			System.out.println("\n非法格式");
    			return false;
    		}
    			
    		return true;
    	}
    
    

    函数首先根据产生式/S→V=E:
    的FIRST集[i],判断当前符号是否为i,赋值表达式的第一个字符一定是变量i,如果是i,则扫描指针加1,得到下一个符号,并且这个符号必须为=,如果不是则打印错误信息,返回false,结束分析。如果是=,则进入非终结符号E的分析函数:

    /**
    	 * E->TR			[(, i]
    	 * @return
    	 */
    	private boolean E() { //E→TR: (,i    ),#
    	
    		String s =get_indexP( indexP);
    		
    		if(s.equals("(")||s.equals("i")) {
    			if(!T())
    				return false;
    			if(!R())
    				return false;
    		}else
    		{
    			System.out.println("算术表达式格式错误");
    			return false;
    		}
    		return true;
    	}
    
    

    同理分析根据产生式E->TR的FIRST集[(, i],
    判断当前符号是否为i或则(,如果是则进入产生式右部的非终结符号的分析函数,也就是T、R的分析函数,如果不是则输出错误提示信息,返回false:

    R的分析函数:

    /**
    	 *  R->ATR			[+, -]
    		R->$			[$]
    		
    		R 			) ; 
    	 * @return
    	 */
    	private boolean R() { //R→ATR|ε:+,-	        ),;
    		String s =get_indexP( indexP);
    		if(s.equals("+")||s.equals("-")) {
    			if(!A())
    				return false;
    			if(!T())
    				return false;
    			if(!R())
    				return false;
    		}else if(s.equals(")")) {
    			return true;
    		}else if(s.equals(";")) {
    			
    			return true;
    		}
    		else
    		{
    			System.out.println("算术表达式格式错误");
    			return false;
    		}
    		
    		return true;
    	}
    
    

    同理分析根据产生式R->ATR的FIRST集[+, -],
    判断当前符号是否为+或-,如果是则进入产生式右部的非终结符号的分析函数,也就是A、T、R的分析函数,与前面不同的是,R的产生式具有空,所以如果当前符号不是+或则-,则要判断是否为R的FOLLOW集的终结符号,R的FOLLOW集为
    ) 、; 两个符号, 判断当前符号是否为)或;
    ,如果是则返回true,不是FIRST集和FOLLOW集的终结符号的,则打印错误信息,返回false。

    同上面几个函数的构造情况,可以构造出所有非终结符号的分析函数,具体的情况的可以查看源程序,下面也有函数的用流程图。

    5.2流程图:

    下面列出了所有非终结符号的分析函数流程图:

    P:

    在这里插入图片描述

    S:

    在这里插入图片描述

    E:

    在这里插入图片描述

    T:

    在这里插入图片描述

    R:

    在这里插入图片描述

    F:

    在这里插入图片描述

    Y:

    在这里插入图片描述

    C:

    在这里插入图片描述

    Z:

    在这里插入图片描述

    B:

    在这里插入图片描述

    I:

    在这里插入图片描述

    X:

    在这里插入图片描述

    O:

    在这里插入图片描述

    A:

    在这里插入图片描述

    M:

    在这里插入图片描述

    Q:

    在这里插入图片描述

    J:

    在这里插入图片描述

    K:

    在这里插入图片描述

    H:

    在这里插入图片描述

    U:

    在这里插入图片描述
    每个流程图,参照各个分析函数进行绘制,其中token可以看做取当前的扫描指针位置的符号,indexP++操作表示扫描指针加1,则下面的token代表加1后的扫描指针位置的符号。其中if,在程序为:8
    ,else为:9,else if为f,逻辑运算符&&为:a,||为o,等等细节在文法中已经说明。

    流程图对应每个非终结符号的分析函数,但是在程序中有具体的实现细节,还要根据设计的数据结构进行分析。

    6、程序测试

    6.1 测试用例1分析成功:

    测试用例为二元式文件结构部分的用例

    结果为:

    在这里插入图片描述

    在这里插入图片描述

    6.2 修改测试用例1使其不符合if语句语法:
    6.2.1缺少 ‘;’符号

    在这里插入图片描述

    输出错误提示:

    在这里插入图片描述

    6.2.1缺少 ‘)’符号

    在这里插入图片描述

    6.2.3缺少if语句程序体:

    在这里插入图片描述
    在这里插入图片描述

    实验总结与反思

    实验过程总结

    递归下降分析法的原理是利用函数之间的递归调用模拟语法树自上而下的构造过程,具体实现方法概述如下:

    1.1 非终结符号分析函数实现步骤

    1)每个非终结符对应一个分析函数;
    2)产生式右部为该产生式左部非终结符所对应分析函数的“函数体”;
    特别的对于产生式能推出空ε的时候,函数体应判断,对应左部非终结符号的FOLLOW集。
    3)产生式右部终结符对应从输入串中“消耗”该终结符的操作;
    在右部
    遇到终结符号的时候,应该将扫描符号向下扫描,也就是往下分析二元式文件内容的操作。
    4)产生式中右部的‘|’对应函数体中的“if-else”语句。
    产生式右部不总是一个,有的多个,这就需要判断当前符号,选择进入的函数体,也就是,LL1文法,所代表的意思,向前分析1步,得到当前前进方向。

    在构造每个非终结符号分析函数的时候,大致就是如上的步骤。具体的细节有很多实现的方法,这也和自己构造的数据结构有关。

    1.2 分析函数错误提示

    在实验结果中设计程序,使函数输出分析过程,如下:

    -------------分析过程-------------------
        P→Q→if J→( E→T→F→C→B→( E→T→F→C→B→( E→T→F→C→B→num1 I→Z→O→>= C→B→num2 I→Z→Y→) I→X→&& B→( E→T→F→C→B→num1 I→Z→Y→A→+ T→F→C→B→num2 I→Z→O→!= C→B→23 I→Z→Y→) I→Z→Y→) I→X→|| B→a I→Z→O→>= C→B→num1 I→Z→Y→) K→
        {U→P→S→
            a = E→T→F→C→B→num1 I→Z→Y→A→+ T→F→C→B→num2 I→Z→Y→;
    U→P→Q→
            if J→( E→T→F→C→B→a I→Z→O→>= C→B→num1 I→Z→Y→) K→
            {U→P→
                ;
    U→        }H→
            else K→
            {U→P→S→
                a = E→T→F→C→B→num1 I→Z→Y→A→+ T→F→C→B→num2 I→Z→Y→;
    U→        }U→P→S→
            a = E→T→F→C→B→num1 I→Z→Y→A→+ T→F→C→B→num2 I→Z→Y→;
    U→    }H→
        else if J→( E→T→F→C→B→num1 I→Z→O→>= C→B→num2 I→Z→Y→) K→
        {U→P→Q→
            if J→( E→T→F→C→B→a I→Z→O→>= C→B→num1 I→Z→Y→) K→
            {U→P→S→
                a = E→T→F→C→B→num1 I→Z→Y→A→+ T→F→C→B→num2 I→Z→Y→;
    U→        }H→
            else K→
            {U→P→S→
                a = E→T→F→C→B→num1 I→Z→Y→A→+ T→F→C→B→num2 I→Z→Y→;
    U→        }U→
        }H→
        else if J→( E→T→F→C→B→num1 I→Z→O→>= C→B→num2 I→Z→Y→) K→;
    H→
        else K→
        {U→
            {U→P→Q→
                if J→( E→T→F→C→B→a I→Z→O→>= C→B→num1 I→Z→Y→) K→S→a = E→T→F→C→B→num1 I→Z→Y→A→+ T→F→C→B→num2 I→Z→Y→;
    H→
                else K→
                {U→P→S→
                    a = E→T→F→C→B→num1 I→Z→Y→A→+ T→F→C→B→num2 I→Z→Y→;
    U→            }U→
            }
        }
    满足该文法
    
    

    具体的实现细节就是在每个分析函数体开始的时候,让其打印当前分析函数的非终结符号,这样就可以得到递归下降的调用各个分析函数的情况。可以看出,分析的时候是有规律的,但是对于LL1递归下降分析函数过程来说,如果分析二元式文件是个正确的程序话,会正常分析下去,但是遇到错误,程序就不能继续往下分析,因为对于LL1文法,向前看1步,但是向前看1步的时候出现错误,有时候可以判断出简单的错误,但是对于复杂的式子,是无法判断当前的被分析程序的出现的具体错误,例如:

    if ( ( ( num1 >= num2 ) ( num1 + num2 != 23 ) ) || a >= num1 ) 
       		 {
            a = num1 + num2 ;
    
            if ( a >= num1 ) 
            {
                ;
            }
            else 
            {
                a = num1 + num2 ;
            }
            a = num1 + num2 ;
        }
    
    

    这个程序if的判断语句有错误的,我们很容易判断出缺少的一个逻辑操作符,但是对于递归下降分析函数来说,它不可能分析整个判断语句,然后再做出判断,它只能往前看1步,这就限制了递归下降分析函数不能进行太多的分析,如下递归下降分析程序的报错结果:

    在这里插入图片描述

    ( ( ( num1 >= num2 ) 这个是if
    语句的判断语句,对于对于递归下降分析过程来说,进入非终结符号J的分析函数,J→(E),然后读取(
    ,然后进入E等等,num2后的)符号之后,递归下降分析函数发现后面操作是*( num1 + num2
    != 23 )要进入下一个E,但是要先结束当前E,但是发现如果结束E的话,也就是要( (
    num1 >= num2 ))*
    ,缺少了一个(,递归下降分析过程,只能分析到这,报错然后退出程序,实现不了太智能的错误提示。

    1.3实验输出分析过程的完善,print_indexp(int i)

    该函数进行了对输出分析过程的格式完善,也就是怎么将二元式文件输出为源程序,输出的时候不仅仅只对源程序考虑为一个字符串进行输出,还要注意到了缩进,也就是程序的对齐,保证其可读性。大致结果就是如下:

     if ( ( ( num1 >= num2 ) && ( num1 + num2 != 23 ) ) || a >= num1 ) 
        {
            a = num1 + num2 ;
    
            if ( a >= num1 ) 
            {
                ;
            }
            else 
            {
                a = num1 + num2 ;
            }
            a = num1 + num2 ;
        }
        else if ( num1 >= num2 ) 
        {
            if ( a >= num1 ) 
            {
                a = num1 + num2 ;
            }
            else 
            {
                a = num1 + num2 ;
            }
        }
        else if ( num1 >= num2 ) ;
    
        else 
        {
            {
                if ( a >= num1 ) a = num1 + num2 ;
    
                else 
                {
                    a = num1 + num2 ;
                }
            }
    }
    
    

    而不是输出一个字符串,可读性非常差,例如下面这样:

    if(((num1>=num2)&&(num1+num2!=23))||a>=num1) { a = num1+num2;
    if(a>=num1) { ; } else { a = num1+num2; } a = num1+num2; } else if
    (num1>=num2) { if(a>=num1) { a = num1+num2; } else { a = num1+num2; } }
    else if (num1>=num2) ; else { { if(a>=num1) a = num1+num2; else { a =
    num1+num2; } } }

    public void print_indexp(int i)
    根据当前字符的情况,进行分析,是否进行格式的输出,主要控制格式的就是tab和换行符,函数根据当前字符的情况,分析出应当先输出几个tab或是否应该输出\n换行符。private
    int
    tab = 1; //制符表的数量,初始为1的作用就是管理tab数量的一个私有变量。

    举个简单的例子,遇到’{‘符号,我们要先考虑当前的tab数量应该输出多少,并且要输出一个\n换行符,然后要改变一下tab的数量,因为按照习惯,遇到一个’{’符号后,在输入语句的时候,我们会进行一个相对tab的输入,所以tab要++。同理
    ‘}’要考虑当前的tab数量应该输出多少,并且要输出一个\n换行符,然后要改变一下tab的数量,但是tab要–。还有很多具体的情况要具体对待,具体的细节可以查看批注或者源程序。

    最后源码地址
    https://github.com/Topdu/Compilation-principle/blob/master/RecursiveAnalysisMain.java

    展开全文
  • 以下选项的叙述正确的是循环队列有队头和队尾两个指针,因此,循环队列是非线性结构在循环队列,只需要队头指针就能反映队列元素的动态变化情况在循环队列,只需要队尾指针就能反映队列元素的动态变化...

    原标题:模拟试卷C【单项选择题】

    1. 按照“后进先出”原则组织数据的数据结构是____

    队列

    双向链表

    二叉树

    2. 以下选项的叙述中,正确的是

    循环队列有队头和队尾两个指针,因此,循环队列是非线性结构

    在循环队列中,只需要队头指针就能反映队列中元素的动态变化情况

    在循环队列中,只需要队尾指针就能反映队列中元素的动态变化情况

    循环队列中元素的个数是由队头指针和队尾指针共同决定

    3. 关于数据的逻辑结构,以下选项中描述正确的是

    存储在外存中的数据

    数据所占的存储空间量

    数据在计算机中的顺序存储方式

    数据的逻辑结构是反映数据元素之间逻辑关系的数据结构

    4. 以下选项中,不属于结构化程序设计方法的是

    自顶向下

    逐步求精

    模块化

    可封装

    5. 以下选项中,不属于软件生命周期中开发阶段任务的是

    软件测试

    概要设计

    软件维护

    详细设计

    6. 为了使模块尽可能独立,以下选项中描述正确的是

    模块的内聚程度要尽量高,且各模块间的耦合程度要尽量强

    模块的内聚程度要尽量高,且各模块间的耦合程度要尽量弱

    模块的内聚程度要尽量低,且各模块间的耦合程度要尽量弱

    模块的内聚程度要尽量低,且各模块间的耦合程度要尽量强

    7. 以下选项中叙述正确的是

    软件交付使用后还需要进行维护

    软件一旦交付就不需要再进行维护

    软件交付使用后其生命周期就结束

    软件维护指修复程序中被破坏的指令

    8. 数据独立性是数据库技术的重要特点之一,关于数据独立性,以下选项中描述正确的是

    数据与程序独立存放

    不同数据被存放在不同的文件中

    不同数据只能被对应的应用程序所使用

    以上三种说法都不对

    9. 以下选项中,数据库系统的核心是

    数据模型

    数据库管理系统

    数据库

    数据库管理员

    10. 一间宿舍可以住多个学生,以下选项中描述了实体宿舍和学生之间联系的是

    一对一

    一对多

    多对一

    多对多

    11. 以下选项中不是Python文件读操作方法的是

    read

    readline

    readall

    readtext

    12. 以下选项中说法不正确的是

    静态语言采用解释方式执行,脚本语言采用编译方式执行

    C语言是静态语言,Python语言是脚本语言

    编译是将源代码转换成目标代码的过程

    解释是将源代码逐条转换成目标代码同时逐条运行目标代码的过程

    13. 以下选项中,不是Python语言特点的是

    强制可读:Python语言通过强制缩进来体现语句间的逻辑关系

    变量声明:Python语言具有使用变量需要先定义后使用的特点

    平台无关:Python程序可以在任何安装了解释器的操作系统环境中执行

    黏性扩展:Python语言能够集成C、C++等语言编写的代码

    14. 拟在屏幕上打印输出“Hello World”,以下选项中正确的是

    print(Hello World)

    print('Hello World')

    printf("Hello World")

    printf('Hello World')

    15. IDLE环境的退出命令是

    exit()

    esc()

    close()

    回车键

    16. 以下选项中,不符合Python语言变量命名规则的是

    keyword_33

    keyword33_

    33_keyword

    _33keyword

    17. 以下选项中,不是Python语言保留字的是

    for

    while

    continue

    goto

    18. 以下选项中,Python语言中代码注释使用的符号是

    //

    /*… …*/

    #

    19. 关于Python语言的变量,以下选项中说法正确的是

    随时命名、随时赋值、随时变换类型

    随时声明、随时使用、随时释放

    随时命名、随时赋值、随时使用

    随时声明、随时赋值、随时变换类型

    20. Python语言提供的3个基本数字类型是

    整数类型、二进制类型、浮点数类型

    整数类型、浮点数类型、复数类型

    整数类型、二进制类型、浮点数类型

    整数类型、二进制类型、复数类型

    21. 以下选项中,不属于IPO模式一部分的是

    Input (输入)

    Program (程序)

    Process (处理)

    Output (输出)

    22. 以下选项中,属于Python语言中合法的二进制整数是

    0b1708

    0B1010

    0B1019

    0bC3F

    23. 关于Python语言的浮点数类型,以下选项中描述错误的是

    浮点数类型与数学中实数的概念一致

    浮点数类型表示带有小数的类型

    Python语言要求所有浮点数必须带有小数部分

    小数部分不可以为0

    24. 关于Python语言数值操作符,以下选项中描述错误的是

    x/y表示x与y之商

    x//y表示x与y之整数商,即不大于x与y之商的最大整数

    x**y表示x的y次幂,其中,y必须是整数

    x%y表示x与y之商的余数,也称为模运算

    25. 以下选项中,不是Python语言基本控制结构的是

    顺序结构

    程序异常

    循环结构

    跳转结构

    26. 关于分支结构,以下选项中描述不正确的是

    if 语句中语句块执行与否依赖于条件判断

    if 语句中条件部分可以使用任何能够产生True和False的语句和函数

    二分支结构有一种紧凑形式,使用保留字if和elif实现

    多分支结构用于设置多个判断条件以及对应的多条执行路径

    27. 关于Python函数,以下选项中描述错误的是

    函数是一段具有特定功能的语句组

    函数是一段可重用的语句组

    函数通过函数名进行调用

    每次使用函数需要提供相同的参数作为输入

    28. 以下选项中,不是Python中用于开发用户界面的第三方库是

    turtle

    PyQt

    wxPython

    pygtk

    29. 以下选项中,不是Python中用于进行数据分析及可视化处理的第三方库是

    numpy

    pandas

    mayavi2

    mxnet

    30. 以下选项中,不是Python中用于进行Web开发的第三方库是

    flask

    Django

    scrapy

    pyramid

    31.

    下面代码的执行结果是

    >>>1.23e-4+5.67e+8j.real

    0.000123

    1.23

    5.67e+8

    1.23e4

    32.下面代码的执行结果是

    >>>s = "11+5in"

    >>>eval(s[1:-2])

    16

    6

    11+5

    执行错误

    33.下面代码的执行结果是

    >>>abs(-3+4j)

    3.0

    4.0

    5.0

    执行错误

    34.下面代码的执行结果是

    >>>x = 2

    >>>x *= 3 + 5**2

    13

    15

    56

    8192

    35.下面代码的执行结果是

    ls=[[1,2,3],[[4,5],6],[7,8]]

    print(len(ls))

    1

    3

    4

    8

    36.下面代码的执行结果是

    a = "Python等级考试"

    b = "="

    c = ">"

    print("{0:{1}{3}{2}}".format(a, b, 25, c))

    ===============Python等级考试

    Python等级考试===============

    >>>>>>>>>>>>>>>Python等级考试

    Python等级考试===============

    37.下面代码的执行结果是:

    ls = ["2020", "20.20", "Python"]

    ls.append(2020)

    ls.append([2020, "2020"])

    print(ls)

    ['2020', '20.20', 'Python', 2020, 2020, '2020']

    ['2020', '20.20', 'Python', 2020]

    ['2020', '20.20', 'Python', 2020, [2020, '2020']]

    ['2020', '20.20', 'Python', 2020, ['2020']]

    38.设city.csv文件内容如下:

    巴哈马,巴林,孟加拉国,巴巴多斯

    白俄罗斯,比利时,伯利兹

    下面代码的执行结果是:

    f = open("city.csv", "r")

    ls = f.read().split(",")

    f.close()

    print(ls)

    ['巴哈马', '巴林', '孟加拉国', '巴巴多斯', '白俄罗斯', '比利时', '伯利兹']

    ['巴哈马', '巴林', '孟加拉国', '巴巴多斯n白俄罗斯', '比利时', '伯利兹']

    ['巴哈马, 巴林, 孟加拉国, 巴巴多斯, 白俄罗斯, 比利时, 伯利兹']

    ['巴哈马', '巴林', '孟加拉国', '巴巴多斯', 'n', '白俄罗斯', '比利时', '伯利兹']

    39.下面代码的执行结果是:

    d = {}

    for i in range(26):

    d[chr(i+ord("a"))] = chr((i+13) % 26 + ord("a"))

    for c in "Python":

    print(d.get(c, c), end="")

    Plguba

    Cabugl

    Python

    Pabugl

    40.给出如下代码:

    while True:

    guess = eval(input())

    if guess == 0x452//2:

    break

    作为输入能够结束程序运行的是

    break

    553

    0x452

    "0x452//2"

    需要标准答案的朋友,请后台留言即可!返回搜狐,查看更多

    责任编辑:

    展开全文
  • C语言,字符串的赋值

    千次阅读 2016-02-04 10:13:00
    原文:... ...C语言,为什么字符串可以赋值给字符指针变量 char *p,a='5'; p=&a; //显然是正确的, p="abcd"; //但为什么也可以这样

    原文:http://www.cnblogs.com/KingOfFreedom/archive/2012/12/07/2807223.html


    一、

    C语言中,为什么字符串可以赋值给字符指针变量

    char *p,a='5';
    p=&a;                     //显然是正确的,
    p="abcd";              //但为什么也可以这样赋值??

    问:一直理解不了为什么可以将字串常量赋值给字符指针变量,请各位指点!
     
    答:
    双引号做了3件事:  
    1.申请了空间(在常量区),存放了字符串 
    2. 在字符串尾加上了'/0'    
    3.返回地址

    你这里就是 返回的地址  赋值给了  p                      


    二、

    char *p = “hello”;

    上边的表达式为什么可以,而把p换成数组,然后再赋值就不行了

    解释:

    字符串常量"hello"出现在一个表达式中时"hello"表达式使用的值就是这些字符所存储的地址(在常量区),而不是这些字符本身。

    所以,可以把字符串赋值给指向字符的指针p,而不能把字符串赋值给一个字符数组。 

    char a[10] = “hello”; //这样可以,这种情况是c语言初始化所支持的

    如果写成char a[10]

    然后 a = “hello” 这样就错误了。  

    同样是a数组,char a[10] = “hello”;这种是数组的初始化,和a[0] = ‘h’ a[1] = ‘e’…是一个道理

    但是换成char a [10]

    然后a = “hello”就不行了 “hello”赋值的值是一个地址,而a虽然也有地址,但是这与指针是不一样的,指针的值是地址,而数组的值虽然也是地址,但是却是一个常量,所以不能给常量赋值。

    代码测试

    #include <stdio.h> 

    int main()

    {

           char *p = "hello";

           printf("%s",p);

           char a[10];

           a = "hello";

           return 0;

    }

    error C2440: '=' : cannot convert from 'char [6]' to 'char [10]'

            There is no context in which this conversion is possible

    看到这样的错误提示,你是否会想到把char a[10]改成char a[6]呢

    试一下,

    error C2106: '=' : left operand must be l-value

    运算符的左边应该是一个“左值”。所谓“左值”就是指在程序中占用内存空间、可以被修改的量,比如各种变量。 

     

    继续扩展问题:

    在使用指针的时候,指针可以自增,而数组不能自增

    编译器给数组分配了空间,数组a的地址就是一个常量了,让常量自增这肯定是不行的。 

    继续扩展:

          在指针自增的时候,编译器会自动识别类型比如指针是指向int型的,想获取下一个的地址时,指针直接p++就行了,不要多此一举的p+4了

          特别需要注意的是,在void指针使用的时候,不能使用指针运算,应为void型编译器不能识别类型的长度(即指针所指对象的体积),p++这样就是不合法的,即不能进行数学运算,也不能使用*取值操作,想使用必须转换为其它的类型


    三、
    标题:对字符数组,字符指针,字符串常量

    1.以字符串形式出现的,编译器都会为该字符串自动添加一个0作为结束符,如在代码中写
      "abc",那么编译器帮你存储的是"abc\0"

    2."abc"是常量吗?答案是有时是,有时不是。

      不是常量的情况:"abc"作为字符数组初始值的时候就不是,如
                      char str[] = "abc";
        因为定义的是一个字符数组,所以就相当于定义了一些空间来存放"abc",而又因为
        字符数组就是把字符一个一个地存放的,所以编译器把这个语句解析为
        char str[3] = {'a','b','c'};
                      又根据上面的总结1,所以char str[] = "abc";的最终结果是
        char str[4] = {'a','b','c','\0'};
        做一下扩展,如果char str[] = "abc";是在函数内部写的话,那么这里
        的"abc\0"因为不是常量,所以应该被放在栈上。
      
      是常量的情况:  把"abc"赋给一个字符指针变量时,如
                      char* ptr = "abc";
        因为定义的是一个普通字符指针,并没有定义空间来存放"abc",所以编译器得帮我们
        找地方来放"abc",显然,把这里的"abc"当成常量并把它放到程序的常量区是编译器
        最合适的选择。所以尽管ptr的类型不是const char*,并且ptr[0] = 'x';也能编译
        通过,但是执行ptr[0] = 'x';就会发生运行时异常,因为这个语句试图去修改程序
        常量区中的东西。
        记得哪本书中曾经说过char* ptr = "abc";这种写法原来在c++标准中是不允许的,
        但是因为这种写法在c中实在是太多了,为了兼容c,不允许也得允许。虽然允许,
        但是建议的写法应该是const char* ptr = "abc";这样如果后面写ptr[0] = 'x'的
        话编译器就不会让它编译通过,也就避免了上面说的运行时异常。
        又扩展一下,如果char* ptr = "abc";写在函数体内,那么虽然这里的"abc\0"被
        放在常量区中,但是ptr本身只是一个普通的指针变量,所以ptr是被放在栈上的,
        只不过是它所指向的东西被放在常量区罢了。

    3.数组的类型是由该数组所存放的东西的类型以及数组本身的大小决定的。
      如char s1[3]和char s2[4],s1的类型就是char[3],s2的类型就是char[4],
      也就是说尽管s1和s2都是字符数组,但两者的类型却是不同的

    4.字符串常量的类型可以理解为相应字符常量数组的类型
      如"abcdef"的类型就可以看成是const char[7]

    5.sizeof是用来求类型的字节数的。如int a;那么无论sizeof(int)或者是sizeof(a)都
      是等于4,因为sizeof(a)其实就是sizeof(type of a)

    6.对于函数参数列表中的以数组类型书写的形式参数,编译器把其解释为普通
      的指针类型,如对于void func(char sa[100],int ia[20],char *p)
      则sa的类型为char*,ia的类型为int*,p的类型为char*


    7.根据上面的总结,来实战一下:
      对于char str[] = "abcdef";就有sizeof(str) == 7,因为str的类型是char[7],
      也有sizeof("abcdef") == 7,因为"abcdef"的类型是const char[7]。
      对于char *ptr = "abcdef";就有sizeof(ptr) == 4,因为ptr的类型是char*。
      对于char str2[10] = "abcdef";就有sizeof(str2) == 10,因为str2的类型是char[10]。
      对于void func(char sa[100],int ia[20],char *p);
      就有sizeof(sa) == sizeof(ia) == sizeof(p) == 4,
      因为sa的类型是char*, ia的类型是int*,p的类型是char*。

    四、

    C语言中字符数组和字符串指针分析,该贴原址:http://www.cnblogs.com/gigikouyi/archive/2006/08/01/464737.html

    这几天搞Unix上的C程序,里面用到了很多字符数组和字符串指针,我记得在学完C语言后相当一段时间里,对指针这个东西还是模模糊糊,后来工作也没怎么用到过C,虽然网上这类的文章也有很多,还是决定自己在这做个小总结,也算加深下自己的印象,写了下面的测试程序:

    #include <stdio.h>

    int main(int argc, char *argv[])
    {

      char day[15] = "abcdefghijklmn";
      char* strTmp = "opqrstuvwxyz";

      printf("&day is %x\n",&day);
      printf("&day[0] is %x\n",&day[0]);
      printf("day is %x\n",day);
      
      printf("\n&strTmp is %x\n",&strTmp);
      printf("&strTmp[0] is %x\n",&strTmp[0]);
      printf("strTmp is %x\n",strTmp);
      
      getchar(); 
      return 0;
    }

    运行后屏幕上得到如下结果:


    其实看到结果估计很多东西就好明白了,

        先看看前三个输出也就是关于变量day的,在 char day[15] = "abcdefghijklmn"; 这个语句执行的时候,系统就分配了一段长15的内存,并把这段内存起名为day,里面的值为"abcdefghijklmn",如下图所示:

            再看程序,第一个输出,&day,&号是地址运算符,也就是day这个变量的内存地址,很明显,在最前面,也就是a字符所在字节的地址;
            对于第二个输出也就好理解了,&day[0],就是day数组中第一个变量(也就是a)的地址,因此他们两个是一样的;
            第三个输出是day,对于数组变量,可以使用变量名来索引变量中的内容,其实这里的day可以理解成数组变量退化的指针,并且指向数组的开头,既然把它理解成指针,那么它的值肯定是地址了,所以他的值和上面两个也一样。

        再看看后面三个输出,关于字符串指针strTmp,在执行char* strTmp = "opqrstuvwxyz";后,内存的图示如下:


    如图所示,内存分配了两段内存,一个名为strTmp,类型是一个字符指针,另外一段是一个字符串常量,且strTmp里面存放着字符常量的首地址,注意这里无法通过strTmp修改这段字符串,因为是常量;于是程序中的后面三个输出就好理解了;
        
      &strTmp:strTmp这个字符指针的地址
      &strTmp[0]:strTmp所指字符常量第一个字符的地址
      strTmp:strTmp这个字符指针的值,即字符常量的首地址

    因此,最后两个的值是一样的。
          指针可以这样理解,指针这种类型,和int,char,double等等是一样的,只是它用来保存地址值的,而int变量保存整数,char变量保存字符,仅此而已,就char型指针或者int指针,本质是一样的,都是存放的地址,只不过那个地址所里面的变量类型不同而已,还有一种void型指针,就是可以放任何类型变量的地址。

    五、个人代码以及注释,纯属个人理解,定有不妥之处,望批评指正:

    #include <stdio.h>

    int main(int argc, char *argv[])
    {
     char* strTmp = "abcd";
     printf("strTmp is %s\n",strTmp);//将字符串常量"abcd"的地址所隐含的内容转换成“string类型”
     printf("strTmp is %d\n",strTmp);//将字符串常量"abcd"的地址转换成int类型,这里不同的机子不同的时间的运行结果可能会不一样,因为地址可能会发生变化
     printf("strTmp is %c\n",strTmp);//将字符串常量"abcd"的地址转换成字符型,这里不同的机子不同的时间的运行结果可能会不一样,因为地址可能会发生变化
     printf("*strTmp is %c\n",*strTmp);//将字符串常量"abcd"的地址所隐含的内容转换成字符型,由下面注释的这句会抛出异常可知,这里并无截取字符串,*strTmp长度本身就是1
     //printf("*strTmp is %s\n",*strTmp);//不能将字符转换成字符串型
     getchar(); 
     return 0;
    }

     六、后来又有看到下面这样的说法可供读者参考:

    1. C语言中没有字符串类型,只有用字符数组来表示。这和c++中string是有区别的,C++中string是可以直接赋值如string s;s="Hello world";但是C语言中的字符数组却不能这样。所以,这里的strTmp可以理解为字符数组的首地址,也可以用它代表整个字符数组,所以能输出所有字符数组中的内容。

     2.字符串就是字符数组或者是指针。 内存实现都一样的。 数组名字就是一个指针。

    char ch[100] ;
    char *p;
    p =ch;

    3.定义的字符串方式举例:

    字符串定义其实很简单在c/c++语言中定义一个字符串可以使用如下的语法:

    char *s1=“string1”;//定义字符串常量,指针形式

    char s2[]=“string2”;//定义字符串常量,数组形式

    char *s3=new char[10];//定义字符串变量并分配内存 指针形式

    strcpy(s3,"string3");//为s3赋值

    char s4[10];//定义字符串变量,数组形式

    strcpy(s4,"string4");//为s4赋值

    以上四种方法都能定义一个字符串,同时通过字符串在内存中的分布可以清楚地知道是什么情况

     

    4. C语言中字符串赋值方法strcpy(char*d,char*s)其中s代表是源字符串,d代表目标字符串,也就是你要赋值的字符串。

    5.c语言中的字符串跟java或c++中的字符串不同。如char *p;其中p是一个指针,p中存储一个内存缓冲区的首地址。所谓的内存缓冲区就是一段连续的内存地址,里面存放了一系列的字符。那系统又是如何判断在哪里结束呢。那就是根据符号‘\0’。这个字符占一个字节,8位,每位的值都是0。


    展开全文
  • verilogassign语句

    万次阅读 2014-08-10 20:12:53
    /************************************** * Module: assign * Date:2014-08-10 ...* Description: verilog的assign语句的用法 ***************************************/ /* 一、引入语法的概念
  • FPGA学习笔记(三)——Verilog HDL基本语句

    千次阅读 多人点赞 2019-10-02 12:17:24
    FPGA学习笔记(三)————Verilog HDL基本语句 文章目录FPGA学习笔记(三...赋值语句连续赋值过程赋值 1.过程语句 Verilog有两种结构化过程语句:initial和always语句,是行为建模的两种基本语句,所有的行为语句...
  • C语言,为什么字符串可以赋值给字符指针变量

    万次阅读 多人点赞 2018-08-15 11:59:11
    C语言,为什么字符串可以赋值给字符指针变量 char *p,a='5'; p=&amp;a; //显然是正确的, p="abcd"; //但为什么也可以这样赋值?? 问:一直理解不了为什么可以将字串常量赋值给字符指针变量,请...
  • .c变量必须定义在执行语句前面

    千次阅读 2015-09-08 13:16:22
    废话不多说,先看下面代码: int main() {  int a =1; a = 2; int b = 3; ...如果你将这段代码保存在.c文件下,在vc++6.0去编译,编译器会报错,但同样的代码保存在...是表示将变量a重新赋值为2,所以是执行语句)之前.
  • linux的变量赋值及改变、显示

    千次阅读 2017-11-12 02:08:00
    一,SHELL本地变量:本地变量就如同局部变量一样,只在本SHELL起作用。它不会影响到其他SHELL的变量。格式:NAME=value1,变量的调用:在变量前加$$ echo $HOME/home/hbwork$ WEEK=Satur$ echo Today is $...
  • MyBatis动态Sql语句

    万次阅读 2015-07-09 08:42:30
    MyBatis对数据库的操作,有时要带一些条件,因此动态SQL语句非常有必要,下面就主要来讲讲几个常用的动态SQL语句的语法 and STU_
  • delphi语句

    千次阅读 2005-11-16 09:22:00
    如果说数据类型是Pascal编程的一个...简单语句和复合语句Pascal 简单语句中不包含任何别的语句,赋值语句和过程调用即是简单语句的例子。简单语句用分号隔开,如下所示:X := Y + Z; // assignmentRandomize; 
  • 这篇文章主要介绍了Python的条件判断语句基础学习教程,文中使用的是Python2.x版本但条件语句部分的使用规则未在3.x改变,需要的朋友可以参考下 if语句用来检验一个条件, 如果 条件为真,我们运行一块语句(称为 ...
  • 1.一种直观的方法   假设现在需要往内存0x12ff7c地址上存入一个整型数0x100。我们怎么才能做到呢? 我们知道可以通过一个指针...所以我们可以用下面的方法: 1 2 int *p = (int *)0x12ff7c; *p = 0
  • 成员是常量或引用:成员无法赋值,只能被初始化2)从效率上: 如果在类构造函数里赋值:在成员初始化时会调用一次其默认的构造函数,在类构造函数里又会调用一次成员的构造函数再赋值 如果在类构造函数使用初始化...
  • 第1章 Python程序设计概述单元测验11、下列选项,不属于Python语言特点的是A、面向对象B、运行效率高C、可读性好D、开源2、以下叙述正确的是A、Python3.x和Python2.x兼容B、Python语言只能以程序方式执行C、Python...
  • JPQL语句

    千次阅读 2018-08-26 16:12:48
    JPQL语言 JPQL语言,即 Java Persistence Query Language 的简称。JPQL 是一种和 SQL 非常类似的... JPQL语言的语句可以是 select 语句、update 语句或delete语句,它们都通过 Query 接口封装执行。   ...
  • SQL常用语句总结

    千次阅读 2012-08-29 09:18:50
     SELECT 语句不能从被插入数据的表中选择行  INSERT INTO 的列数必须与SELECT 语句返回的列数相等  INSERT INTO 的数据类型要与SELECT 语句返回的数据类型相同 insert select 语句的另外一个用处对表...
  • C语言for语句用法详解

    千次阅读 多人点赞 2015-12-15 20:18:57
    求解表达式2,若其值为真(非0),则执行for语句中指定的内嵌语句,然后执行下面第3)步;若其值为假(0),则结束循环,转到第5)步。求解表达式3。转回上面第2)步继续执行。循环结束,执行for语句下面的一
  • ASP使用SQL-select语句

    千次阅读 2006-11-06 13:59:00
    MySQL、SQL Server和mSQL都是绝佳的SQL工具,可惜,在ASP的环境下你却用不着它们来创建实用的SQL语句。不过,你可以利用自己掌握的Access知识以及相应的Access技能,再加上我们的提示和技巧,相信一
  • MyBatis对数据库的操作,有时要带一些条件,因此动态SQL语句非常有必要,下面就主要来讲讲几个常用的动态SQL语句的语法 MyBatis用于实现动态SQL的元素主要有: if choose(when,otherwise) trim where ...
  •  如果出现上面这种情况就容易发生局部析构的危险,当然在上面这个例子中是不会出现局部析构的危险了,但是如果在Derive中有一个指针指向了堆中的一块内存,此时如果仅仅调用Base类中的析构函数,则就不会执行这个...
  • python: 条件语句、断言

    千次阅读 2018-01-21 21:58:20
    语句块 1、语句块并非一种语句语句块是一组满足一定条件时执行...3、在python冒号( : )用来表示语句块的开始,语句的每一个语句都需要缩进(缩进量相同),当退回到和已经闭合的块一样的缩进时,表示当前语句...
  • 现在前端的UI框架有很多,通常vuejs会使用element UI,(个人觉得这个框架真的超好用)但使用框架的不好之处就在于有些东西比较难以把控,这里介绍一下使用datepicker遇到的小问题以及我的解决办法,希望能给大家...
  • Verilog HDL数据类型的选择

    千次阅读 2008-07-13 17:54:00
    •信号可以分为端口信号和内部信号...若输出端口在过程块中赋值则为register类型;若在过程块外赋值(包括实例化语句),则为net类型。 •内部信号类型与输出端口相同,可以是net或register类型。判断方法也与输出端口相

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 80,801
精华内容 32,320
关键字:

下面选择中正确的赋值语句是