Pointer arithmetic - counting dots - c

Can't get any of the if statements to trigger and print something in the main. I am confused on what to do from here.
Can you please find where I have gone wrong for this problem. I need to count the number of dots and dashes (I've simplified it to work on the dots first).
The question must be solved using pointer arithmetic and the function has been provided
void analyse(char* code, int* p_dots, int* p_dashes);
^
#include <stdio.h>
#include <string.h>
void analyse(char* code, int* p_dots)
{
char *current = code;
int k = 0;
if ((*current + k) == '.')
{
p_dots++;
k++;
}
if ((*current + k) == '-')
{
//p_dashes++;
k++;
}
if ((*current + k) ==' ')
{
//p_dashes++;
k++;
}
}
int main(void)
{
char* morse[] =
{
"... --- ..." // SOS
// "-- --- .-. ... .", // MORSE
// "-.-. --- -.. .", // CODE
// "-.-. --- -- .--. ..... ----- -----", // COMP500
// ". -. ... . ..... ----- .----" // ENSE501
};
char* code = *morse;
int*p_dots=0;
//int*p_dashes = 0;
analyse(code, p_dots);
printf("Line 1 has %d dots", *p_dots);
return 0;
}

Here's another approach:
#include <stdio.h>
void analyse(char *code, int *p_dots, int *p_dashes)
{
for( ; *code ; ++code)
{
if (*code == '.')
*p_dots += 1;
if (*code == '-')
*p_dashes += 1;
}
}
int main(void)
{
int dots;
int dashes;
char *morse[] =
{
"... --- ...", // SOS
"-- --- .-. ... .", // MORSE
"-.-. --- -.. .", // CODE
"-.-. --- -- .--. ..... ----- -----", // COMP500
". -. ... . ..... ----- .----" // ENSE501
};
for(int i = 0 ; i < sizeof(morse) / sizeof(char *) ; ++i)
{
dots = 0;
dashes = 0;
analyse(morse[i], &dots, &dashes);
printf("Line %d ('%s') has %d dots and %d dashes\n", i, morse[i], dots, dashes);
}
return 0;
}
A pointer is a variable which contains the address of something. Using a pointer you can look at and/or change the thing it points to. If you increment the pointer, you're changing the address in the pointer, and thus you're changing what it points to.
In this code all of the pointer arithmetic is done in the for loop for( ; *code ; ++code) in the analyse function. What we're saying here is that A) there's no initialization section (there's no code before the first ; in the for statement); B) we want to continue as long as what's being pointed at the the code pointer is not zero *code in the "test" portion of the for statement; and C) after each pass through the loop we want to increment the code pointer (++code in the "increment" section of the for statement).
If you wanted to you could replace the for loop in analyse with:
while(*code <> 0)
{
if (*code == '.')
*p_dots += 1;
if (*code == '-')
*p_dashes += 1;
code += 1;
}
When looking at code like this I find it useful to mentally say "object of pointer" whenever I see an asterisk used with a pointer variable - so I'd read the first line of the code above as
While object of pointer "code" is not equal to zero...
then the next lines read as
if object of pointer "code" is equal to the character "period"
then add one to object of pointer "p_dots"
if object of pointer "code" is equal to the character "dash"
then add one to object of pointer "p_dashes"
add one to the variable "code"
Instead of "object of pointer" maybe you could read it as "target of pointer" to remind yourself that the pointer is pointing to something, and you're manipulating what the pointer points to, or in other words you're manipulating the pointers "target".
I find this kind of thing helps me understand pointer-based code a little bit more. Perhaps that'll help you too.

Change all occurrences of this:
(*current + k) == '-'
to this:
*(current + k) == '-'
(which means the same as the following:)
current[k] = '-'
Also in main() your code needs to update the value of an int, so just create a regular int, then pass it's address:
int p_dots = 0;
...
analyse(code, &p_dots);//sends the address so the value can be updated
Also, to get all of the values summed properly in the void analyse(char* code, int* p_dots) analyze function, a while loop is necessary: The following is your code with a while loop, and some simplifications: (it only tracks p_dots)
void analyse(char* code, int* p_dots)
{
char *current = code;
int k = 0;
while(*current)//check for NULL character
{
if (*current == '.')
{
(*p_dots)++;
}
if (*current == '-')
{
k++;
}
if (*current ==' ')
{
k++;
}
current++; //increment pointer to next position
}
}
By the way, one way to track each of the symbols ( ., -. ) is to create a struct, then pass it's address to update the member values. The example code for this is below:
typedef struct {
int dot;
int dash;
int space;
} count_s;
void analyse(char* code, count_s *c)//new prototype to track `.`, `-` and ` `
{
char *current = code;
int k = 0;
while(*current)
{
if (*current == '.')
{
(*c).dot++;
}
if (*current == '-')
{
(*c).dash++;
}
if (*current ==' ')
{
(*c).space++;
}
current++;
}
}
int main(void)
{
char* morse[] =
{
"... --- ..." // SOS
// "-- --- .-. ... .", // MORSE
// "-.-. --- -.. .", // CODE
// "-.-. --- -- .--. ..... ----- -----", // COMP500
// ". -. ... . ..... ----- .----" // ENSE501
};
char* code = *morse;
//int p_dots;
count_s count;
//int*p_dashes = 0;
analyse(code, &count);
printf("Line 1 has %d dots\n", count.dot);
printf("Line 1 has %d dashes\n", count.dash);
printf("Line 1 has %d spaces\n", count.space);
return 0;
}
EDIT to address question in comments: How would I go about doing it for the following lines that I've commented out?
Create an array of string literals
const char *morse[] = {{"... --- ..."},
{"-- --- .-. ... ."},
{"-.-. --- -.. ."},
{"-.-. --- -- .--. ..... ----- -----"},
{". -. ... . ..... ----- .----"}};
New main()...
int main(void)
{
char* code = NULL;//will be used to point to each array line.
count_s count = {0};//instance of struct with accumulators
// place declaration of const char *morse[] here as illustrated above
for(int i=0;i<sizeof(morse)/sizeof(morse[0]);i++)
{
code = morse[i];//set pointer to successive array elements 0-4
analyse(code, &count);//count will accumulate values as loop progresses.
}
printf(" %d dots\n", count.dot);
printf(" %d dashes\n", count.dash);
printf(" %d spaces\n", count.space);
return 0;
}

You have issue on the increment of dots and on scanning the string, see p_dots update on main.
I change p_dots to an int to have it on the stack (should be renamed I think). On your code you need to allocate some memory.
To scan the string:
Just increment the pointer to read char by char in an infinite loop, use a switch for the future of scanning '-', stop on null char.
code is pointing to the beginning of the string, switch (*code++) will read the current char and move pointer to the next one.
Start:
"... --- ..."
^
code
Read . and move code
"... --- ..."
^
code
and so on
#include <stdio.h>
#include <string.h>
void analyse(char* code, int* p_dots)
{
while (42) {
switch (*code++) {
case '.':
(*p_dots)++;
break;
case '-':
break;
case '\0':
return;
default:
break;
}
}
}
int main(void)
{
char* morse[] =
{
"... --- ..." // SOS
// "-- --- .-. ... .", // MORSE
// "-.-. --- -.. .", // CODE
// "-.-. --- -- .--. ..... ----- -----", // COMP500
// ". -. ... . ..... ----- .----" // ENSE501
};
char* code = *morse;
int p_dots=0;
//int*p_dashes = 0;
analyse(code, &p_dots);
printf("Line 1 has %d dots\n", p_dots);
return 0;
}

Related

Bison: When I try to add action in function grammar rule, the segmentation fault 11 occur

I have a problem about segmentation fault 11.
Every time, when I want to add action rules in function grammar blocks, I must get the segmentation fault 11.
Therefore, I cannot get the dump.out, which is a file that record the identifiers for me.
I do not think the problem is because of the scanner file, but that is still possible.
Of course, the problem should have something about symbol table, but it is really strange.
The problem just occurs like:
function: FN ID '(' ')' {if ($2->st_type == UNDEF) $2->st_type = FUNCTION_TYPE};
When I add action in the block, segmentation fault 11 will occur.
However, this is okay.
function: FN ID '(' ')' {};
The parser file do not contains all contents since it is so many.
I use mac os
I hope someone can help me.
Thank you anyway
Where the error occur
1: // Hello World Example
<fn>
<id: main>
<(>
<)>
<{>
2: fn main() {
3: // Print text to the console
<let>
<mut>
<id: a>
<:>
<int>
<=>
<integer: 10>
<;>
4: let mut a:int = 10;
<let>
<mut>
<id: b>
<=>
<string: 1199>
<;>
5: let mut b = "1199";
<let>
<mut>
<id: sum>
<[>
<str>
<,>
<integer: 10>
<]>
<;>
6: let mut sum[str, 10];
<id: sum>
<[>
<integer: 0>
<]>
<=>
<string: 100>
<;>
7: sum[0] = "100";
<id: b>
<=>
<string: 123>
<+>
<id: b>
<;>
8: b = "123" + b;
<println>
<(>
<string: Hello World>
<)>
<;>
9: println ("Hello World");
<}>
10: }
Symbol table:
a
b
sum
main
Segmentation fault: 11
The input file
// Hello World Example
fn main() {
// Print text to the console
let mut a:int = 10;
let mut b = "1199";
let mut sum[str, 10];
sum[0] = "100";
b = "123" + b;
println ("Hello World");
}
This is my symbol table header file.
#include <stdio.h>
/* maximum size of hash table */
#define SIZE 211
/* maximum size of tokens-identifiers */
#define MAXTOKENLEN 40
/* token types */
#define UNDEF 0
#define INT_TYPE 1
#define REAL_TYPE 2
#define STR_TYPE 3
#define LOGIC_TYPE 4
#define ARRAY_TYPE 5
#define FUNCTION_TYPE 6
/* new type for parser */
#define CONST_INT_TYPE 7
#define CONST_REAL_TYPE 8
#define CONST_STR_TYPE 9
#define CONST_LOGIC_TYPE 10
/* how parameter is passed */
#define BY_VALUE 1
#define BY_REFER 2
/*
* Originally here, now it is in the symbols.c
* current scope
* int cur_scope = 0;
*/
/* parameter struct */
typedef struct Parameter{
int par_type;
char *param_name;
// to store value
int ival; double fval; char *st_sval; int bval; // boolean type
int passing; // value or reference
struct Parameter *next; // link to next one
}Param;
/* a linked list of references (lineno's) for each variable */
typedef struct Ref{
int lineno;
struct Ref *next;
int type;
}RefList;
// struct that represents a list node
typedef struct list{
char st_name[MAXTOKENLEN];
int st_size;
int scope;
RefList *lines;
// to store value and sometimes more information
int st_ival; double st_fval; char *st_sval; int st_bval;
// type
int st_type;
int inf_type; // for arrays (info type) and functions (return type)
// array stuff
int *i_vals; double *f_vals; char **s_vals; int *b_vals; // boolean type
int array_size;
// function parameters
Param *parameters;
int num_of_pars; // Meanwhile, it record the current position of the parameters
// pointer to next item in the list
struct list *next;
}list_t;
/* the hash table */
static list_t **hash_table;
// Function Declarations
void create(); // initialize hash table
unsigned int hash(char *key); // hash function for insert
void insert(char *name, int len, int type, int lineno); // insert entry
list_t *lookup(char *name); // search for entry
list_t *lookup_scope(char *name, int scope); // search for entry in scope
void hide_scope(); // hide the current scope
void incr_scope(); // go to next scope
void dump(FILE *of); // dump file
This is symbol table code file.
#include "symbols.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* current scope */
int cur_scope = 0;
void create()
{
int i;
hash_table = malloc(SIZE * sizeof(list_t*));
for(i = 0; i < SIZE; i++) hash_table[i] = NULL;
}
unsigned int hash(char *key)
{
unsigned int hashval = 0;
for(;*key!='\0';key++) hashval += *key;
hashval += key[0] % 11 + (key[0] << 3) - key[0];
return hashval % SIZE;
}
void insert(char *name, int len, int type, int lineno)
{
unsigned int hashval = hash(name); // hash function used
list_t *l = hash_table[hashval];
while ((l != NULL) && (strcmp(name,l->st_name) != 0)) l = l->next;
/* variable not yet in table */
if (l == NULL){
l = (list_t*) malloc(sizeof(list_t));
strncpy(l->st_name, name, len);
/* add to hashtable */
l->st_type = type;
l->scope = cur_scope;
l->lines = (RefList*) malloc(sizeof(RefList));
l->lines->lineno = lineno;
l->lines->next = NULL;
l->next = hash_table[hashval];
hash_table[hashval] = l;
// printf("Inserted %s for the first time with linenumber %d!\n", name, lineno); // error checking
}
/* found in table, so just add line number */
else{
l->scope = cur_scope;
RefList *t = l->lines;
while (t->next != NULL) t = t->next;
/* add linenumber to reference list */
t->next = (RefList*) malloc(sizeof(RefList));
t->next->lineno = lineno;
t->next->next = NULL;
// printf("Found %s again at line %d!\n", name, lineno);
}
}
list_t *lookup(char *name)
{ /* return symbol if found or NULL if not found */
unsigned int hashval = hash(name);
list_t *l = hash_table[hashval];
while ((l != NULL) && (strcmp(name,l->st_name) != 0)) l = l->next;
return l; // NULL is not found
}
list_t *lookup_scope(char *name, int scope)
{ /* return symbol if found or NULL if not found */
unsigned int hashval = hash(name);
list_t *l = hash_table[hashval];
while ((l != NULL) && (strcmp(name,l->st_name) != 0) && (scope != l->scope)) l = l->next;
return l; // NULL is not found
}
void hide_scope()
{ /* hide the current scope */
if(cur_scope > 0) cur_scope--;
}
void incr_scope()
{ /* go to next scope */
cur_scope++;
}
/* print to stdout by default */
void dump(FILE * of)
{
int i; int count; // record whether first line prints or not.
fprintf(of,"------------ ----------------- -------------\n");
fprintf(of,"Name Type Line Numbers\n");
fprintf(of,"------------ ----------------- -------------\n");
for (i=0; i < SIZE; ++i){
if (hash_table[i] != NULL){
list_t *l = hash_table[i];
while (l != NULL){
RefList *t = l->lines;
fprintf(of,"%-12s ",l->st_name);
printf("%s\n", l->st_name); // print out all the names in the symbol table
if (l->st_type == INT_TYPE) fprintf(of,"%-7s","int");
else if (l->st_type == REAL_TYPE) fprintf(of,"%-7s","real");
else if (l->st_type == STR_TYPE) fprintf(of,"%-7s","string");
else if (l->st_type == LOGIC_TYPE) fprintf(of,"%-7s","bool");
else if (l->st_type == CONST_INT_TYPE) fprintf(of, "%-7s", "const_int"); // constant_int_type
else if (l->st_type == CONST_REAL_TYPE) fprintf(of, "%-7s", "const_real"); // constant_real_type
else if (l->st_type == CONST_STR_TYPE) fprintf(of, "%-7s", "const_string"); // constant_string_type
else if (l->st_type == CONST_LOGIC_TYPE) fprintf(of, "%-7s", "const_bool"); // const_logic_type
else if (l->st_type == ARRAY_TYPE){
fprintf(of,"array of ");
if (l->inf_type == INT_TYPE) fprintf(of,"%-7s","int");
else if (l->inf_type == REAL_TYPE) fprintf(of,"%-7s","real");
else if (l->inf_type == STR_TYPE) fprintf(of,"%-7s","string");
else if (l->inf_type == LOGIC_TYPE) fprintf(of,"%-7s","bool");
else fprintf(of,"%-7s","undef");
}
else if (l->st_type == FUNCTION_TYPE){
fprintf(of,"%-7s %s","function returns ");
if (l->inf_type == INT_TYPE) fprintf(of,"%-7s","int");
else if (l->inf_type == REAL_TYPE) fprintf(of,"%-7s","real");
else if (l->inf_type == STR_TYPE) fprintf(of,"%-7s","string");
else if (l->inf_type == LOGIC_TYPE) fprintf(of,"-7%s","bool");
else fprintf(of,"%-7s","undef");
}
else fprintf(of,"%-7s","undef"); // if UNDEF or 0
count = 0;
while (t != NULL){
if (count == 0)
{
if (l->st_type == INT_TYPE || l->st_type == REAL_TYPE || l->st_type == STR_TYPE || l->st_type == UNDEF)
fprintf(of,"%13d ", t->lineno);
else if (l->st_type == CONST_INT_TYPE || l->st_type == CONST_REAL_TYPE || l->st_type == CONST_STR_TYPE || l->st_type == CONST_LOGIC_TYPE)
fprintf(of,"%10d", t->lineno);
else if (l->st_type == ARRAY_TYPE || l->st_type == FUNCTION_TYPE)
fprintf(of,"%4d", t->lineno);
}
else
fprintf(of,"%3d", t->lineno);
count++;
t = t->next;
}
fprintf(of,"\n");
l = l->next;
}
}
}
}
scanner file
%option noyywrap
%{
#include "symbols.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "parser.tab.h"
#define LIST strcat(buf,yytext)
#define token(t) {LIST; printf("<%s>\n");}
#define tokenInteger(t, i) {LIST; printf("<%s: %d>\n", t, i);}
#define tokenReal(t, r) {LIST; printf("<%s: %lf>\n", t, r);}
#define tokenString(t, s) {LIST; printf("<%s: %s>\n", t, s);}
#define MAX_LINE_LENG 256
extern FILE* yyin;
extern FILE* yyout;
int linenum = 1;
char buf[MAX_LINE_LENG];
char* tempStr;
int indexForStr = 1;
list_t* temp;
%}
%x Comment
%%
"//".* {LIST;}
"/*" {BEGIN(Comment); LIST;}
<Comment>"*/" {LIST; BEGIN(0);}
<Comment>\n {LIST; printf("%d: %s\n", linenum++, buf); buf[0] = '\0';}
<Comment>. {LIST;}
"bool" {
token("BOOL");
return BOOL;
}
"break" {token("BREAK"); return BREAK;}
"char" {token("CHAR"); return CHAR;}
"continue" {token("CONTINUE"); return CONTINUE;}
"do" {token("DO"); return DO;}
"else" {token("ELSE"); return ELSE;}
"enum" {token("ENUM"); return ENUM;}
"extern" {token("EXTERN"); return EXTERN;}
"false" {token("FALSE"); yylval.boolVal = 0; return FALSE;}
"float" {
token("FLOAT");
return FLOAT;
}
"for" {token("FOR"); return FOR;}
"fn" {token("FN"); return FN;}
"if" {token("IF"); return IF;}
"in" {token("IN"); return IN;}
"int" {
token("INT");
return INT;
}
"let" {token("LET"); return LET;}
"loop" {token("LOOP"); return LOOP;}
"match" {token("MATCH"); return MATCH;}
"mut" {token("MUT"); return MUT;}
"print" {token("PRINT"); return PRINT;}
"println" {token("PRINTLN"); return PRINTLN;}
"pub" {token("PUB"); return PUB;}
"return" {token("RETURN"); return RETURN;}
"self" {token("SELF"); return SELF;}
"static" {token("STATIC"); return STATIC;}
"str" {
token("STR");
return STR;
}
"struct" {token("STRUCT"); return STRUCT;}
"true" {token("TRUE"); yylval.boolVal = 1; return TRUE;}
"use" {token("USE"); return USE;}
"where" {token("WHERE"); return WHERE;}
"while" {token("WHILE"); return WHILE;}
"," {token("','"); return ',';}
":" {token("':'"); return ':';}
";" {token("';'"); return ';';}
"(" {token("'('"); return '(';}
")" {token("')'"); return ')';}
"[" {token("'['"); return '[';}
"]" {token("']'"); return ']';}
"{" {token("'{'"); return '{';}
"}" {token("'}'"); return '}';}
"+" {token("'+'"); return '+';}
"-" {token("'-'"); return '-';}
"*" {token("'*'"); return '*';}
"/" {token("'/'"); return '/';}
"++" {token("'++'"); return '++';}
"--" {token("'--'"); return '--';}
"%" {token("'%'"); return '%';}
"<" {token("'<'"); return LESS;}
"<=" {token("'<='"); return '<=';}
">=" {token("'>='"); return '>=';}
">" {token("'>'"); return GREATER;}
"==" {token("'=='"); return '==';}
"!=" {token("'!='"); return '!=';}
"&&" {token("'&&'"); return '&&';}
"||" {token("'||'"); return '||';}
"!" {token("'!'"); return EXCLAMATION;}
"=" {token("'='"); return ASSIGN;}
"+=" {token("'+='"); return '+=';}
"-=" {token("'-='"); return '-=';}
"*=" {token("'*='"); return '*=';}
"/=" {token("'/='"); return '/=';}
"->" {token("'->'"); return ARROW;}
"read" {token("'READ'"); return READ;}
[A-Z_a-z]([A-Z_a-z]|[0-9])* {
insert(yytext, yyleng, UNDEF, linenum);
yylval.symptr = lookup(yytext);
tokenString("id", yylval.symptr->st_name);
return ID;
}
"0"|[0-9][0-9]* {
sscanf(yytext, "%d", &yylval.intVal);
tokenInteger("integer", yylval.intVal);
return INTEGER;
}
[0-9_]+"."[0-9_]|[0-9_][Ee][+-]?[0-9_]+ {
yylval.floatVal = atof(yytext);
tokenReal("real", yylval.floatVal);
return REAL;
}
\"([\\.]|[^\\"])*\" {
tempStr = malloc((strlen(yytext) - 1) * sizeof(char));
for (int i = 0; i < strlen(yytext) - 2; i++)
{
tempStr[i] = yytext[indexForStr];
indexForStr++;
}
tempStr[strlen(yytext) - 1] = '\0';
yylval.stringVal = strdup(yytext);
tokenString("string", tempStr);
free(tempStr);
indexForStr = 1;
return STRING;
}
\n {
LIST;
printf("%d: %s", linenum++, buf);
buf[0] = '\0';
}
[ \t]* {LIST;}
. {
LIST;
printf("%d:%s\n", linenum+1, buf);
printf("bad character:'%s'\n",yytext);
exit(-1);
}
%%
parser file
%{
#include "symbols.c"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#define Trace(t, line) printf(t, line) // Trace where the error occurs and print the line number
#ifndef STRSIZE
#define STRSIZE 40
#endif
#ifndef PARAMSIZE
#define PARAMSIZE 40
#endif
extern FILE* yyin;
extern FILE* yyout;
extern int linenum;
extern int yylex();
void yyerror(char* msg);
%}
%union{
char* stringVal;
double floatVal;
int intVal;
int boolVal;
list_t* symptr;
}
/* tokens */
%token <symptr> ID
%token <intVal> INTEGER
%token <floatVal> REAL
%token <stringVal> STRING
%token <boolVal> TRUE FALSE
%token INT FLOAT STR BOOL
%token BREAK CHAR CONTINUE DO ELSE
%token ENUM EXTERN FOR
%token FN IF IN LET
%token LOOP MATCH MUT PRINT PRINTLN
%token RETURN SELF STATIC STRUCT
%token USE WHERE WHILE
%token READ PUB
%token LESS GREATER ASSIGN EXCLAMATION ARROW
/* precedence for operators */
%left '||'
%left '&&'
%left EXCLAMATION
%left LESS '<=' '>=' GREATER '==' '!='
%left '+' '-'
%left '*' '/'
%left UMINUS
/* types */
%type <intVal> integer_exp
%type <floatVal> real_exp
%type <stringVal> string_exp
%type <boolVal> bool_exp
%start program /* the initial entry point */
%%
program: functions | global_declaration functions
;
global_declaration: global_declaration constant_declaration
| global_declaration variable_declaration
| global_declaration array_declaration
| constant_declaration
| variable_declaration
| array_declaration
;
local_declaration: local_declaration constant_declaration
| local_declaration variable_declaration
| local_declaration array_declaration
| constant_declaration
| variable_declaration
| array_declaration
;
block: start local_declaration statements end
| start local_declaration end
| start statements end
| start end
;
start: '{' {
incr_scope();
}
end: '}' {
hide_scope();
}
;
functions: functions function
| function
;
function: FN ID '(' ')' start local_declaration statements end{
if ($2->st_type == UNDEF)
{
$2->st_type = FUNCTION_TYPE;
$2->inf_type = UNDEF;
}
else
{
Trace("line %d: Redeclaration of identifier.\n", linenum);
}
}
| FN ID '(' ')' start statements end {
if ($2->st_type == UNDEF)
{
$2->st_type = FUNCTION_TYPE;
$2->inf_type = UNDEF;
}
else
{
Trace("line %d: Redeclaration of identifier.\n", linenum);
}
}
| FN ID '(' ')' start local_declaration end {
if ($2->st_type == UNDEF)
{
$2->st_type = FUNCTION_TYPE;
$2->inf_type = UNDEF;
}
else
{
Trace("line %d: Redeclaration of identifier.\n", linenum);
}
}
| FN ID '(' ')' start end {
if ($2->st_type == UNDEF)
{
$2->st_type = FUNCTION_TYPE;
$2->inf_type = UNDEF;
}
else
{
Trace("line %d: Redeclaration of identifier.\n", linenum);
}
;
%%
void yyerror(char* msg)
{
fprintf(stderr, "line %d: %s\n", linenum, msg);
}
int main(int argc, char** argv)
{
/* create the hash table */
create();
/* open the source program file */
if (argc != 2) {
printf ("Usage: sc filename\n");
exit(1);
}
yyin = fopen(argv[1], "r"); /* open input file */
int flag;
flag = yyparse();
/* perform parsing */
if (flag == 1) /* parsing */
yyerror("Parsing error !"); /* syntax error */
fclose(yyin); /* close input file */
/* output symbol table */
printf("\nSymbol table:\n");
yyout = fopen("dump.out", "w");
dump(yyout);
fclose(yyout);
return 0;
}
Clearly, the problem occurs during the dump function when the token type is FUNCTION_TYPE. That's clear from the debugging output (dump is presumably executing when the segfault occurs) and from the change report (the problem occurs when an action sets the st_type field to FUNCTION_TYPE).
Visual inspection of the if clause in dump() corresponding to that condition reveals the following obvious error:
fprintf(of,"%-7s %s","function returns ");
That call to fprintf has a format string with two %s conversions. However, there is only one argument to be inserted.
The real question you should be asking yourself is "How can I easily find stupid typos like this without spending a lot of time or resorting to outside experts?"
As a first approximation, that error is so common and easy to detect that most compilers will warn you about it. So your first step is to make sure you always compile with warnings enabled (-Wall if you are using gcc or clang), and that you read the warnings.
Even without the warning, it would have been straight-forward to find the error with a debugger such as gdb. Just set a breakpoint at dump and single-step until the segfault occurs.
Finally, you are making your life much more complicated when you first build a large complex program with a lot of components and only then start to debug it. In the long run, you will find that it is worth taking the time to test each component individually (your symbol table, for example), using some kind of test harness, and only assembling your more complex program when you are reasonably confident that the individual pieces work. That will avoid the difficulty of identifying where the error occurred (as in this case, where you were evidently distracted by your doubts about the parser generator, leading you to miss the actual problem which has nothing to do with the parser).
By the way, strncpy is a definite red flag, although in this case you seem to have been lucky (or unlucky) enough to not encounter the bug. strncpy is pretty well never what you want, and if it is what you want then the length parameter should be the longest string you can accommodate rather than the length of the input string. (strncpy is intended for use in fixed-length formats, which is why it pads the output to the specified length.)
If you use the length of the input string, then you have two problems: (1) the copy is guaranteed to not be NUL-terminated, leading to Undefined Behaviour; and (2) nothing stops the copy from overrunning the output buffer, in the case that the input string is too long.
Even used correctly, strncpy requires you to manually NUL-terminate the output, which is a nuisance. A better solution is to first check that the string is not too long (length < SIZE) and then use strcpy, which will correctly NUL-terminate. Even better is to make the name field a char* instead of an array, and dynamically allocate a string of the correct length (see strdup, for example), thereby avoiding having to arbitrarily limit the size of identifiers.

reverse vowels in string in c

I'm a beginner programmer. I was trying out the problem of reversing vowels in a string.
Ex: input: zabedfigu, output: zubidfega
When I run the following code, I get a runtime error. I've tried to change the conditions within incrementing the pointer pc1 upto only upto the middle index etc., but that either gives me a runtime error or doesn't give me the required output. I'd like some help on what to do to make my code work as well as any new way of solving the problem. TIA.
#include<stdio.h>
char* reverseVowels(char* str)
{
char *pc1, *pc2;
int i;
pc1 = &str[0];
for(i=0; str[i]!='\0';++i)
;
pc2 = &str[i-1];
while(pc1!=pc2)
{
if((*pc1=='a')||(*pc1=='e')||(*pc1=='i')||(*pc1=='o')||(*pc1=='u'))
{
while(pc2!=pc1)
{
if((*pc2=='a')||(*pc2=='e')||(*pc2=='i')||(*pc2=='o')||(*pc2=='u'))
{
char temp;
temp = *pc1;
*pc1 = *pc2;
*pc2 = temp;
++pc2;
break;
}
else
++pc2;
}
++pc1;
}
else
++pc1;
}
//return str;
return NULL;
}
int main()
{
char string[20], *pstr;
scanf("%s", string);
//pstr = reverseVowels(string);
//printf("%s", pstr);
reverseVowels(string);
printf("%s", string);
return 0;
}
You have several answers and comments pointing out the fundamental flaw in your code — that you're incrementing pc2 instead of decrementing it. However, I think your algorithm is more complicated than need be. You could:
Subject at all times to pc1 < pc2:
If pc1 is not pointing at a vowel, increment it
If pc2 is not pointing at a vowel, decrement it
If the pointers are different, swap the vowels and adjust the pointers
With test code, and with the addition of an is_vowel() function which detects both upper-case and lower-case vowels, I ended up with:
#include <ctype.h>
#include <stdio.h>
#include <string.h>
static inline int is_vowel(int c)
{
c = tolower(c);
return (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u');
}
static void reverse_vowels(char *string)
{
char *p1 = string;
char *p2 = p1 + strlen(p1); // '\0' is not a vowel
while (p1 < p2)
{
while (p1 < p2 && !is_vowel((unsigned char)*p1))
p1++;
while (p1 < p2 && !is_vowel((unsigned char)*p2))
p2--;
if (p1 != p2)
{
char c = *p1;
*p1++ = *p2;
*p2-- = c;
}
}
}
int main(void)
{
#ifdef INTERACTIVE
char line[1024];
while (fgets(line, sizeof(line), stdin) != NULL)
{
line[strcspn(line, "\n")] = '\0';
printf("Input: [%s]\n", line);
reverse_vowels(line);
printf("Output: [%s]\n", line);
}
#else
char strings[][40] =
{
"",
"a",
"b",
"ab",
"abe",
"abeci",
"nnnnummmmmmmmmmmmippppoqq",
"AbleWasIEreISawElba",
"A Man, A Plan, A Canal - Panama!"
};
enum { NUM_STRINGS = sizeof(strings) / sizeof(strings[0]) };
for (int i = 0; i < NUM_STRINGS; i++)
{
printf("Input: [%s]\n", strings[i]);
reverse_vowels(strings[i]);
printf("Output: [%s]\n", strings[i]);
}
#endif /* INTERACTIVE */
return 0;
}
You can compile it with -DINTERACTIVE to give you an interactive test, or by default it gives a fixed set of tests.
Default output:
Input: []
Output: []
Input: [a]
Output: [a]
Input: [b]
Output: [b]
Input: [ab]
Output: [ab]
Input: [abe]
Output: [eba]
Input: [abeci]
Output: [ibeca]
Input: [nnnnummmmmmmmmmmmippppoqq]
Output: [nnnnommmmmmmmmmmmippppuqq]
Input: [AbleWasIEreISawElba]
Output: [ablEWasIerEISawelbA]
Input: [A Man, A Plan, A Canal - Panama!]
Output: [a Man, a Plan, a CAnal - PAnamA!]
Sample interactive session (my program was called rv61):
$ rv61
Input: []
Output: []
a
Input: [a]
Output: [a]
b
Input: [b]
Output: [b]
ab
Input: [ab]
Output: [ab]
ae
Input: [ae]
Output: [ea]
abcde
Input: [abcde]
Output: [ebcda]
ablewasiereisawelba
Input: [ablewasiereisawelba]
Output: [ablewasiereisawelba]
palindromic nonsense
Input: [palindromic nonsense]
Output: [pelendromic nonsinsa]
vwlsmssng
Input: [vwlsmssng]
Output: [vwlsmssng]
AManAPlanACanal-Panama!
Input: [AManAPlanACanal-Panama!]
Output: [aManaPlanaCAnal-PAnamA!]
a big and complex sentence with multiple words of a number of lengths and so on
Input: [ a big and complex sentence with multiple words of a number of lengths and so on ]
Output: [ o bog and cemplox sentunca woth moltepli wurds if e nember ef longths and si an ]
$
Note that the testing tests a number of degenerate cases — an empty string, a string with no vowels, a string with one vowel, etc. The palindromic tests benefit from supporting mixed case — it's hard to spot that vowels have been swapped if they're all lower case and the text is a palindrome.
Another test that could be applied is to reverse the vowels twice; the output should be the same as the input. Conservation tests can be important. (If you had a sort which didn't preserve all the elements in the array but added random new ones and/or dropped initial ones, you wouldn't be happy. But that's a topic for another day.)
Having a simple test harness along the lines shown can be helpful for library functions. Many of my library functions have a #ifdef TEST … #endif at the end to allow them to be tested for sanity. The best tests verify that the result is what is expected; these ones are lazy and leave it to visual inspection to validate the output. If it was a library function, there'd be a header to declare the function which would be #included in the source, and the function would not be static. (My default compilation options require either a declaration of the function before it is defined, or the function must be static. I make functions static in sample code like this since there's no other file referencing the function, so there's no need for a header to declare the function, and only headers should declare externally visible functions.)
Note too that the is_vowel name is carefully chosen to avoid the reserved names in the C standard:
Function names that begin with either is or to, and a lowercase letter may be added to the declarations in the <ctype.h> header.
Using isVowel() would have been OK too; using isvowel() would be using a reserved name.
Bill Woodger commented:
Why the comment about '\0' not being a vowel? …
With the code as shown, after the initialization of p2, it is true that *p2 == '\0'. The observation the '\0' is not a vowel matters if the string is non-empty because if it matched the is_vowel() predicate, the null byte could be moved to some point earlier in the string, truncating it.
Suppose the function was reverse_controls() instead of reverse_vowels() and the test used iscntrl() instead of is_vowel(). Then the code would have to handle it differently for a non-zero length string because the null byte would be reported as a control character and that would send things awry — it would be swapped with the first other control character (if there was another) in the string, truncating the string. That is not what's intended.
The problem here is that you are incrementing both pointers the one in the 0 position, and the one in the end position. The first pointer should increment, and the second one should decrement, thus instead of doing this:
++pc2;
You should do this
--pc2;
The problem is occurring when you are going to increment the pointer variable value of pc2, instead of decrementing the pointer variable pc2 value like this --pc.
Updated
According to cleblanc's comment, my previous answer was not working for an input like abade, so then I changed the code to fix that problem.
#include <stdio.h>
char* reverseVowels(char* str)
{
char *pc1, *pc2;
int i;
pc1 = &str[0];
for(i=0; str[i]!='\0';++i)
;
pc2 = &str[i-1];
while(pc1<pc2)
{
if((*pc1=='a')||(*pc1=='e')||(*pc1=='i')||(*pc1=='o')||(*pc1=='u'))
{
while(pc2!=pc1)
{
if((*pc2=='a')||(*pc2=='e')||(*pc2=='i')||(*pc2=='o')||(*pc2=='u'))
{
char temp;
temp = *pc1;
*pc1 = *pc2;
*pc2 = temp;
--pc2;
break;
}
else
--pc2;
}
++pc1;
}
else
++pc1;
}
//return str;
return NULL;
}
int main()
{
char string[20], *pstr;
scanf("%s", string);
//pstr = reverseVowels(string);
//printf("%s", pstr);
reverseVowels(string);
printf("%s\n", string);
return 0;
}

RPN outputting incorrect data: C

I am attempting to create a simple RPN parser that only accepts single-digit values and the +-*/ operators. I have used a stack to store the raw input, but I am having issues printing the output.
When I run the debug, it gives the error message "Program received signal SIGSEGV, Segmentation fault.", tied to line 94. the Input I used in this case was 11+. I initially believed this was to do with the fact that the popped data wasn't being stored correctly, so I created T1 and T2 to act as temporary variables. However this does not fix the issue. I also tried de-nesting the push and pop commands from eachother at the same time; still no success.
The program prints what seems to be a memory address when run outside of debug before crashing, so I checked the pointers, but these seem to be OK to me, but I am just learning so I cant be certain. Thanks in advance!
the lib.c file is here:
#include "defs.h"
//Initialising the stack
TopStack *initTOS()
{
TopStack *pTopStack;
pTopStack=(TopStack*)malloc(sizeof(TopStack));
return(pTopStack);
}
//Pushing an element onto the stack
void push( TopStack *ts, int val)
{
if(ts->num==0)
{
Stack *pNewNode;
pNewNode=(Stack*)malloc(sizeof(Stack));
pNewNode->val=val;
pNewNode->next=NULL;
ts->top=pNewNode;
ts->num++;
}
else if(ts->num!=0)
{
Stack *pNewNode;
pNewNode=(Stack*)malloc(sizeof(Stack));
pNewNode->val=val;
pNewNode->next=ts->top;
ts->top=pNewNode;
ts->num++;
}
}
int pop(TopStack *ts)
{
if(ts->num==0)
{
printf("Can't pop, stack is empty!\n");
exit(1);
}
else{
Stack *pTemp = ts->top;
int RemovedValue;
RemovedValue=pTemp->val;
ts->top=pTemp->next;
ts->num--;
free(pTemp);
return (RemovedValue);
}
}
void testStack(TopStack *ts)
{
int RemovedValue;
push(ts,1);
push(ts,2);
printf("the popped value was %i\n",pop(ts));
printf("the popped value was %i\n",pop(ts));
}
void parseRPN(TopStack *st)
{
char Input[50];
int i;
do{
printf("please enter an expression in single-digit integers using RPN:\n");
scanf("%49s",&Input);
if (strlen(Input)>=50)
{
printf("that expression was too large for the RPN engine to handle! please break it down into smaller sub-tasks.\n");
fflush(stdin);
continue;
}
break;
}while(true);
for (i=0; Input[i] != '\0';i++)
{
if ((isdigit(Input[i])==0) && ((Input[i] != '+') && (Input[i] != '-') && (Input[i] != '*') && (Input[i] != '/')))
{
printf("Error: Invalid operand to RPN\nExiting...\n");
exit(1);
}
else printf("accepted %c for processing...\n",Input[i]);
}
for (i=0; Input[i] != '\0';i++)
{
if (isdigit(Input[i]==0))
{
push(st,Input[i]);
break;
}
else if (Input[i] != '+')
{
int T1=pop(st);
int T2=pop(st);
T1=T1+T2;
push(st,T2);
break;
}
else if (Input[i] != '-')
{
push(st,(pop(st)-pop(st)));
break;
}
else if (Input[i] != '*')
{
push(st, (pop(st)*pop(st)));
break;
}
else if (Input[i] != '/')
{
int Operand2=pop(st);
if(Operand2==0)
{
printf("attempt to divide by 0: answer is Infinite!\n");
exit(0);
}
else
{
push(st,pop(st)/Operand2);
break;
}
}
}
}
void printStack(TopStack *ts)
{
int i;
printf("\a\nThe current content of the stack is\n");
for(ts->num=ts->num;ts->num!=0;ts->num--)
{
printf("%i",ts->top->val);
break;
}
}
Here is defs.h (I can't change this as part of the assignment, it was given to me):
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <assert.h>
#include <stdbool.h>
#define MAX_EXPR 50
//struct that contains stack's element
typedef struct stack_elem{
int val;
struct stack_elem *next;
} Stack;
//struct that contains the pointer to the top of the stack
typedef struct{
int num;//num of elements in stack
Stack *top;;//top of stack
} TopStack;
//ts=pointer to the top of stack, val=element to push
void push( TopStack *ts, int val); //push element on the stack
//prints the elements in the stack
void printStack(TopStack *ts);
// initialize the structure that will point to the top of the stack
TopStack *initTOS();
// a simple test for the stack
void testStack(TopStack *ts);
// ts=pointer to the top of stack
int pop(TopStack *ts);//returns element from top of stack
// simple parser function for RPN expressions that assumes numbers have only one digit
void parseRPN(TopStack *st);
// empties the stack using the pop operation
void emptyStack(TopStack *ts);
// performs the operation defined by character op on the elements on top of stack
void performOp(TopStack *st, char op);
and here is main.c:
#include "defs.h"
int main()
{
TopStack *tp;
tp=initTOS();// initialize the top of stack structure
// testStack(tp);// this function tests your stack
parseRPN(tp);
printStack(tp);
return EXIT_SUCCESS;
}
Where looking in your source code, I have detected the following errors:
Error 1: In parseRPN(), a series of errors in the if-condition isdigit().
if (isdigit(Input[i])!=0) // typo error and bad test
{
push(st,(Input[i]-'0')); // add the decimal value instead of ASCII value
continue; // to check the next input, use continue instead of break
}
Instead of
if (isdigit(Input[i]==0))
{
printf("push(%c),",Input[i]);
push(st,(Input[i]-'0'));
break;
}
Error 2: In parseRPN(), a series of errors in the "+"operator.
else if (Input[i] == '+') // error in '+' comparison
{
int T1=pop(st);
int T2=pop(st);
T1=T1+T2;
push(st,T1); // push the result T1 instead of 2nd arg T2
continue; // to check the next input, use continue instead of break
}
Instead of
else if (Input[i] != '+')
{
int T1=pop(st);
int T2=pop(st);
T1=T1+T2;
push(st,T2);
break;
}
Error 3: In parseRPN(), a series of errors in the "-"operator.
else if (Input[i] == '-') // error in '-' comparison
{
push(st,(pop(st)-pop(st))); // WARNING: not sure it is the good order
continue; // to check the next input, use continue instead of break
}
Error 4: In parseRPN(), a series of errors in the "*"operator.
else if (Input[i] == '*') // error in '*' comparison
{
push(st, (pop(st)*pop(st)));
continue; // to check the next input, use continue instead of break
}
Error 5: In parseRPN(), a series of errors in the "/"operator.
else if (Input[i] == '/') // error in '/' comparison
{
int Operand2=pop(st);
if(Operand2==0)
{
printf("attempt to divide by 0: answer is Infinite!\n");
system("pause");
exit(0);
}
else
{
push(st,pop(st)/Operand2);
continue; // to check the next input, use continue instead of break
}
}
Error 6: In printStack(), replacing for-loop by a while to display all values in the stack.
Stack *pTemp;
pTemp = ts->top; // start of stack
while (pTemp!=NULL) {
printf("%d,",pTemp->val); // display one item value
pTemp = pTemp->next; // explore all the stack
}
Instead of
for(ts->num=ts->num;ts->num!=0;ts->num--)
{
printf("%i",ts->top->val);
break;
}

Printing an array of structs in C

I'm trying to print an array of structs that contain two strings. However my print function does not print more than two indices of the array. I am not sure why because it seems to me that the logic is correct.
This is the main function
const int MAX_LENGTH = 1024;
typedef struct song
{
char songName[MAX_LENGTH];
char artist[MAX_LENGTH];
} Song;
void getStringFromUserInput(char s[], int maxStrLength);
void printMusicLibrary(Song library[], int librarySize);
void printMusicLibraryTitle(void);
void printMusicLibrary (Song library[], int librarySize);
void printMusicLibraryEmpty(void);
int main(void) {
// Announce the start of the program
printf("%s", "Personal Music Library.\n\n");
printf("%s", "Commands are I (insert), S (sort by artist),\n"
"P (print), Q (quit).\n");
char response;
char input[MAX_LENGTH + 1];
int index = 0;
do {
printf("\nCommand?: ");
getStringFromUserInput(input, MAX_LENGTH);
// Response is the first character entered by user.
// Convert to uppercase to simplify later comparisons.
response = toupper(input[0]);
const int MAX_LIBRARY_SIZE = 100;
Song Library[MAX_LIBRARY_SIZE];
if (response == 'I') {
printf("Song name: ");
getStringFromUserInput(Library[index].songName, MAX_LENGTH);
printf("Artist: ");
getStringFromUserInput(Library[index].artist, MAX_LENGTH);
index++;
}
else if (response == 'P') {
// Print the music library.
int firstIndex = 0;
if (Library[firstIndex].songName[firstIndex] == '\0') {
printMusicLibraryEmpty();
} else {
printMusicLibraryTitle();
printMusicLibrary(Library, MAX_LIBRARY_SIZE);
}
This is my printing the library function
// This function will print the music library
void printMusicLibrary (Song library[], int librarySize) {
printf("\n");
bool empty = true;
for (int i = 0; (i < librarySize) && (!empty); i ++) {
empty = false;
if (library[i].songName[i] != '\0') {
printf("%s\n", library[i].songName);
printf("%s\n", library[i].artist);
printf("\n");
} else {
empty = true;
}
}
}
I think the problem is caused due to setting : empty = true outside the for loop and then checking (!empty) which will evaluate to false. What I am surprised by is how is it printing even two indices. You should set empty = false as you are already checking for the first index before the function call.
The logic has two ways to terminate the listing: 1) if the number of entries is reached, or 2) if any entry is empty.
I expect the second condition is stopping the listing before you expect. Probably the array wasn't built as expected (I didn't look at that part), or something is overwriting an early or middle entry.
you gave the definition as:
typedef struct song
{
char songName[MAX_LENGTH];
char artist[MAX_LENGTH];
}Song;
the later, you write if (library[i].songName[i] != '\0') which really seems strange: why would you index the songname string with the same index that the lib?
so I would naturally expect your print function to be:
// This function will print the music library
void printMusicLibrary (Song library[], int librarySize) {
for (int i = 0; i < librarySize; i ++) {
printf("%s\n%s\n\n", library[i].songName,
library[i].artist);
}
}
note that you may skip empty song names by testing library[i].songName[0] != '\0' (pay attention to the 0), but I think it would be better not to add them in the list (does an empty song name make sens?)
(If you decide to fix that, note that you have an other fishy place: if (Library[firstIndex].songName[firstIndex] == '\0') with the same pattern)

Remove the duplicate from a String Using Pointers

#include<stdio.h>
char *removedps(char *x)
{
int Ar[256] = {0};
int ip=0;
int op=0;
char temp;
while(*(x+ip))
{
temp = (*(x+ip));
if (!Ar[temp]) {
Ar[temp] = 1;
*(x+ip) = *(x+op);
op++;
}
ip++;
*(x+op) = '\0';
}
return x;
}
int main()
{
char lo[] = "0001";
printf("%s",removedps(lo));
}
My code is not working
I have tried hard to see the error
All I GET IS the first character .
My idea is simple
make an array of 256 places
insert Zero into them
Then insert 1 for each character inside the string (on that position of the array)
your assignment looks to be the error here.
op is "out postiion", ip is "in position"
so it should be
*(x+op) = *(x+ip);
not the other way.
because *(x+op) = '\0';
is always run every iteration of the loop.
I'd probablly do it more like this ( using your method, which I probablly wouldn't use personally)
char *removedps(char *x)
{
int Ar[256] = {0};
char* start = x;
while(*x)
{
if (Ar[*x])
{ // remove the repeated character
memmove(x, x+1, strlen(x));
}
else
{
Ar[*x] = 1;
x++;
}
}
return start;
}
also, I'd name it remove_duplicate_chars or something, not a fan of cryptic abbreviations.
At the end of the loop, you do *(x+op)='\0';, and then, in the next iteration, you do *(x+ip)=*(x+op);, so from the 2sd iteration, you put there 0.
try do something like:
for (op=ip=0;x[ip];ip++) {
if (!Ar[x[ip]]++) x[op++]=x[ip];
}
x[op]=0;

Resources