C Programming : pointers/char array - c

I have a project that entails us as a programmer, to translate a line of assembly language into machine language and calculate the decimal. We have our own sample computer with opcodes to make it simpler to calculate. My question is if the following function does what I aim to do.
In the main function, I have an array of chars labeled char binary[3] since the value returned will be three bits. Also, in the function is a char opcode[MAXLINELENGTH] which reads in from a file properly the opcode line so I point to it with *string within the function. Will this correctly find the match and fill the binary array in main with the proper 3 digit bit code?
//function should return true if the proper binary output was successfuly copied with the matching opcode
int opcodeBinary(char *string,char *binary){
if(strncmp(*string,"add"){
*binary="000";
return 1;
}
else if(strncmp(*string,"nand"){
*binary="001";
return 1;
}
else if(strncmp(*string,"lw"){
*binary="010";
return 1;
}
else if(strncmp(*string,"sw"){
*binary="011";
return 1;
}
else if(strncmp(*string,"beq"){
*binary="100";
return 1;
}
else if(strncmp(*string,"jalr"){
*binary="101";
return 1;
}
else if(strncmp(*string,"halt"){
*binary="110";
return 1;
}
else if(strncmp(*string,"noop"){
*binary="111";
return 1;
}
else{
return 0;
}
}

No -- in fact, it shouldn't even compile. (Did you try compiling your code before posting it here?)
strncmp and strcmp return 0 if the strings are equal (and strncmp takes three arguments, not two!). To compare two strings for equality, you want something like:
if (strncmp(string, "nand", 4) == 0) {
...
Or, more simply:
if (strcmp(string, "nand") == 0) {
...
Since the string literal "nand" sets a limit on the number of characters to be compared, there's not much point in using strncmp rather than strcmp`.
Both arguments to strcmp (or the first two arguments to strncmp) are of type char*. string is already of type char*; assuming it points to (the first character of) a string, just pass string, not *string, as the first argument to strcmp.
*binary = "000"; shouldn't even compile. binary = "000"; would compile, but it wouldn't do what you want; it's a pointer assignment, and it only affects the value of binary, which is local to your function; there's no visible effect once the function returns.
You can either make the second argument to your function a char**; then the caller can do something like:
char *binary;
if (opcodeBinary(some_string, &binary) {
/* binary now points to a string like "000" */
}
Or the caller can allocate space for the string:
char binary[5]; /* or whatever size you need */
and then inside your function, replace the assignment by a call to strcpy:
strcpy(binary, "000");
You can't return an array from a function; there are several ways you can accomplish the same thing indirectly, but they're all somewhat tedious.

Your function opCodeBinary does not typecheck. When assigning *binary="000", the left hand side is an lvalue of type char and the right hand side is a char*. The same is true for the first operand to strncmp. You should not prepend a * when using strings in C.
What I assume you are required to do is to generate the binary opcode that corresponds to an assembly command. You should read the command from string and write the opcode to binary. There are several things that you do wrong here:
You don't have a way to move the pointers string and binary, as you consume characters from your input and produce new binary opcodes to your output. This would be useful if you were to call the function opCodeBinary repeatedly.
I would suggest that you use double pointers for both parameters to opCodeBinary.
I assume you should not produce opcode 000 in ASCII representation (that is, the string "000") but one byte that contains it in binary form (or, worse, just three bits).
I'd suggest the following for your opCodeBinary function. It correctly resolves (1) and (2) above, but it assumes that each opcode goes to one or more (full) bytes in your binary.
int opCodeBinary (char **string, char **binary)
{
if (strncmp(*string, "add", 3) == 0) {
*string += 3;
*(*binary)++ = '\x00';
return 1;
}
...
}

using wrong strcnmp:
Passing character(dereferenced char pointer) instead of char pointer(string)
you need to pass the number of characters to compare, as a third argument.
Recommendation: Use a hash, to map keywords to binary codes

Related

How to check arguments passed in command line in C?

I am writing a program in C for a basic calculator. I am trying to do this using what I have learned so far: printf() and scanf() functions. I am passing arguments into my program through the command line. I am assuming three arguments will be passed at a time which includes: first int, an operator, and the second int. I want to check if the second arg passed is an operator and then check if it's +,-,*... so on. Here is what I came up with:
int main(int argc, char **argv) {
scanf("%d %c %d", &a, &oper, &b);
if (oper != 43) {
printf("Error: Operator is not a +");
return(1);
}
}
So obviously, I have omitted a lot of the code and kept the relevant part. Here I am just checking if the oper is a +. The ASCII key is 43 so I thought this would work but no luck! Any ideas? (I would like to see if I can do this just with printf and scanf if possible)
EDIT: For example if 12 b 13 was entered, it should return the error above. Same goes for '10 +a 10' or '10 ++ 10'.
Firstly I would highly recommend looking at the man-pages for any C library function you come across, they have a lot of useful information. It seems like you are using scanf() improperly as it is not made to be used with command line arguments.
You can check for matches for a single character by comparing the argument like this:
if(argv[2][0] == '+') ...
(argv[0] is the program's file name).
If would would like to compare string you can use strcmp(). But for the operator example you can get away with just checking the first and second characters in the argument like this:
if(argv[2][0] == '+' && argv[2][0] == '\0') ...
What this does is compare the first two characters of the argument. It first checks for the '+' and then checks if that is the end of the string with by checking for the null terminator '\0'.
We can make the assumption that any argument has at least two characters, the visible character and a null terminator. Performing this on other strings has no guarantee of this however.
The other characters, specifically the numbers need to be converted from their respective ASCII values to integers. You can use atoi or strtol to do this, although atoi will most likely be easier for you.
As David C. Rankin pointed out, **argv is a double pointer which at a high level and in most cases you can treat as a double array. In C a string is actually just an array of type char, so what argv[2] is doing above is first accessing the third index of **argv, this is now de-referenced to a type char * where the string (char array) is located. This can then further be de-referenced by the [0] in argv[2][0] to look at the first char of the string.
Code example:
char **my_arrays = argv; // a array of arrays
char *array = *argv; // de-references to index 0 in argv
char *array = *(argv + 1); // de-references to index 1 in argv
char *array = argv[0]; // de-references to index 0 in argv
char *array = argv[1]; // de-references to index 1 in argv
char first_char = *(*argv) // the first char of the first array of argv
char first_char = *(argv[0]) // the same as above
char first_char = argv[0][0] // the same as above
A side note. All strings in C should end in a null terminator which can be represented by NULL, 0, or '\0' values. This will represent the end of the string and many C functions rely on this to know when to stop.
Also NULL is technically a C macro, but you don't need to treat it any differently than 0 because it literally just expands to 0.
It's char **argv. As Some programmer dude said, you should reread your book/tutorial.
scanf doesn't read arguments. It reads from stdin.
Arguments are of type char* and are stored in argv. To convert these arguments to integers, use atoi or strtol (preferably strtol). See this for more info.
If you want to read from stdin using scanf, that is fine, and what you have will work as long as you instead input the data into stdin, and not as command line arguments.

Convert entire array of characters into integer in C

I'm reading input from a file and trying to create a numerical value from the strings I take in.
I tried simply using the atoi but that doesn't work on characters.
Then I tried using a forloop over my array of characters but then I got error because some characters are actually integers.
Then I tried using ifstatement to check if the characters themselves are integers and just add it to my "sum" manually.
But so far all I get is errors and errors, I'm not sure where my logic is wrong.
In C an array is simply a pointer right? So to access the value at a certain index I use *arr[num] right?
This is my code
char newlineC;
char input[14];
while(fscanf(fp,"%s%c",input, &newlineC)!=EOF){
int val = 0;
int x;
for(x=0; x<14; x++){
if(isdigit(*input[x])){
val = val + input[x];
}else{
int p = atoi(input[x]);
val = val + p;
}
}
I've tried the strol function... didn't work either. I've been at this for so long I feel dumb that I am stumped on something that seems so simple. Any help is appreciated.
You are passing the wrong types all over the place.
char input[14];
this declares an char array of dimension 14. input[i] is the ith char in
the array, it has type char. It's not a pointer, you cannot dereference it,
that's why *input[x] fails. In fact the compiler should have given you an
error there, this error:
invalid type argument of unary β€˜*’ (have β€˜int’)
The same problem with atoi. It expects a pointer to char that points to a
string. input[x] is single char, you cannot pass to atoi. Again the
compiler should have warned you.
fscanf(fp,"%s%c",input, &newlineC)
This is very clumsy. If the input is larger than 13 characters, you will
overflow the buffer. A better way would be:
fscanf(fp, "%13s%c", input, &newline);
Or even better
int val;
fscanf(fp, "%d", &val);
Another error: if you know that input[x] is a digit, then the integer that the
digit represent is input[x] - '0'. So this should be the calculation:
val = val + input[x] - '0';
Overall I would use fgets and strtol:
while(fgets(input, sizeof input, fp))
{
long int val;
char *tmp;
val = strtol(line, &tmp, 0);
if(*tmp == 0 || *tmp == '\n')
printf("An integer was read: %ld\n", val);
else
printf("More than an integer was read: '%s'\n", line);
}
If you are only converting the chars [1..0] to an integer value, all you have to do is
int main(void) {
char input[14];
scanf("%s", input);
if (isdigit(input[0])) {
int num = atoi(input);
printf("%d\n", num);
}
else {
printf("INPUT ERROR\n");
}
}
Are you wanting to process alphabet characters as well and turn them into some integer value?
Arrays in C are based on pointers, but that's not all they are. Arrays in C is just a bunch of those data types in a line in memory. That way you can just access the pointer of the lead variable, than hop down that list in order to get the next iteration of the array.
isdigit(*input[14])
This line will cause issues. Look at what input itself is. input is the pointer to your first element in that array. input is essentially saying char* input = &array[0]; So lets say you dereference that input variable without that 14, you would get the first element. So we can say that *input = array[0]; Do you see the issue here? You basically dereferenced it twice. If you had just done insdigit(input[14]) that would work a bit better.
But onto the bigger issue here. You're taking a char array, that contains only chars, and you're trying to convert them into numbers. Remember that char and int are two different data types. Go ahead and check out this table: https://upload.wikimedia.org/wikipedia/commons/d/dd/ASCII-Table.svg
Recall that chars are basically just numbers that correspond to that ASCII value. For example your computer doesn't read a letter as a D, it reads it as 68 (or the binary format of 68). For numbers it's the same concept, even if it seems like it's just a number and you should be able to add it to val, you'd first have to subtract 48 or use the atoi function on digits.
So what can you do here? I can't say for sure without knowing exactly what you're trying to do as I don't know your specific needs, but just realize that you can already convert char into ints very easily. I believe you can just add a char to an int, although I may be mistaken (I do know there's a very easy way to add a char's value though, maybe you have to cast it first?) However recall that if you want the digits to count for face value, you'd have to subtract 48 from them first.
If you want to use atoi you can, however honestly I don't see the need here, since you're already converting regular chars to numbers here. It'd be sufficient to check to see if the char value is between 48 and 58 (or whatever the actual numbers are) and if they are then you could subtract that.
Hope this helped!

atoi ignores a letter in the string to convert

I'm using atoi to convert a string integer value into integer.
But first I wanted to test different cases of the function so I have used the following code
#include <stdio.h>
int main(void)
{
char *a ="01e";
char *b = "0e1";
char *c= "e01";
int e=0,f=0,g=0;
e=atoi(a);
f=atoi(b);
g=atoi(c);
printf("e= %d f= %d g=%d ",e,f,g);
return 0;
}
this code returns e= 1 f= 0 g=0
I don't get why it returns 1 for "01e"
that's because atoi is an unsafe and obsolete function to parse integers.
It parses & stops when a non-digit is encountered, even if the text is globally not a number.
If the first encountered char is not a space or a digit (or a plus/minus sign), it just returns 0
Good luck figuring out if user input is valid with those (at least scanf-type functions are able to return 0 or 1 whether the string cannot be parsed at all as an integer, even if they have the same behaviour with strings starting with integers) ...
It's safer to use functions such as strtol which checks that the whole string is a number, and are even able to tell you from which character it is invalid when parsing with the proper options set.
Example of usage:
const char *string_as_number = "01e";
char *temp;
long value = strtol(string_as_number,&temp,10); // using base 10
if (temp != string_as_number && *temp == '\0')
{
// okay, string is not empty (or not only spaces) & properly parsed till the end as an integer number: we can trust "value"
}
else
{
printf("Cannot parse string: junk chars found at %s\n",temp);
}
You are missing an opportunity: Write your own atoi. Call it Input2Integer or something other than atoi.
int Input2Integer( Str )
Note, you have a pointer to a string and you will need to establish when to start, how to calculate the result and when to end.
First: Set return value to zero.
Second: Loop over string while it is not null '\0'.
Third: return when the input character is not a valid digit.
Fourth: modify the return value based on the valid input character.
Then come back and explain why atoi works the way it does. You will learn. We will smile.

Begginger, string in switch in C with sourcecode. Possible?

So basically I wanted to create a program in C, in wich you would input 2 character long string (mix of letter and noumber ex.r1,u2,i3,i4,r6) to be the input in my program. Later I want to put this string in SWITCH. Is this possible?
Here's my simple sourcecode. Please correct me on any mistakes :)
#include <stdio.h>
int main(void)
{
char string[2];
scanf("%s", &string);
switch (string)
{
case 'u1' :printf("%s\n", string);break;
default :printf("ERROR");break;
}
return 0;
}
Create a code based on the string and switch on that.
#define Code(a,b) (a + 256*b)
char string[3]; // 3 not 2
if (scanf("%2s", string) != 1) { // No &
Handle_Error();
}
int scode = Code(string[0], string[1]);
switch (scode) {
case Code('u', '1') : printf("%s\n", string); break;
case Code('r', '2') : printf("r2\n"); break;
...
default :printf("ERROR");break;
}
A switch(x) needs an integer value for x and string is an array. So the original approach will not work.
The program can use an integer based on the string for x and use the same method for generating the case values. Since there is only the first 2 char of the string are of interest, the int value is unique.
No, this is not possible. Switch only works with integral types in C (int, short, long, etc, as well as types defined with enum).
You can however use a simple if-else construct to get the same behavior:
if (strcmp(string, "ui" ) == 0) //test for string equality
{
printf("%s\n", string);
}
else
{
printf("ERROR")
}
We use strcmp instead of == because we are dealing pointers which almost certainly not compare equal even when the two strings have the same content.
strcmp(str1, str2) == 0 is the standard idoim in C for comparing two strings.
strcmp returns an integer representing how two strings compare to each other. 0 means they are equal, a negative number means that the first string is lexographically "less than" the second, and a positive number means that the first string is lexographically "greater than" the second. More info can be found here.
A switch won't work here.
You need to use an if/else if construct and strcmp to compare the strings.
Also, you need at least 3 characters in your input array so that it can hold the two input characters and the terminating null character.
Of course, such a small buffer can easily overflow.

Comparing 2 Strings, one in a struct other not C programming

I have this database and I Need to check whether a Product Name is already in the database otherwise I ask the user to input another one.
The problem is this:
I'm trying to compare a string (the Product Name) found inside the struct with the string the user inputs.
The coding of the struct, the user input part and the search method are here below:
product Structure
typedef struct
{
char pName[100];
char pDescription [100];
float pPrice;
int pStock;
int pOrder;
}product;
the checkProduct method:
int checkProduct (char nameCheck[100])
{
product temp;
p.pName = nameCheck;
rewind (pfp);
while (fread(&temp,STRUCTSIZE,1,pfp)==1)
{
if (strcmp (temp.pName,p.pName))
{
return 1;
}
}
return 0;
}
and the user input part [part of the code]:
char nameCheck[100];
gets (nameCheck);
checkProduct (nameCheck);
while (checkProduct == 1)
{
printf ("Product Already Exists!\n Enter another!\n");
while (getchar() !='\n')
{
continue;
}
}
p.pName = nameCheck;
Now I am having the following errors (I Use ECLIPSE):
on the line
while (checkProduct == 1) [found in the user input] is giving me:
"comparison between pointer and integer - enabled by default" marked by a yellow warning triangle
p.pName = nameCheck; is marked as a red cross and stopping my compiling saying:
"incompatible types when assigning to type 'char [100] from type 'char*'
^---- Is giving me trouble BOTH in the userinput AND when I'm comparing strings.
Any suggestions how I can fix it or maybe how I can deference it? I can't understand why in the struct the char pName is being marked as '*' whereas in the char[100] it's not.
Any brief explanation please?
Thank you in advance
EDIT: After emending the code with some of below:
THIS Is the INPUT NAME OF PRODUCT section;
char *nameCheck;
nameCheck = "";
fgets(nameCheck,sizeof nameCheck, stdin);
checkProduct (nameCheck);
int value = checkProduct (nameCheck);
while (value == 1)
{
printf ("Product Already Exists!\n Enter another!\n");
while (getchar() !='\n')
{
}
}
strcpy (p.pName, nameCheck);
this is the new checkName method
int checkProduct (char *nameCheck)
{
product temp;
strcpy (p.pName, nameCheck);
rewind (pfp);
while (fread(&temp,STRUCTSIZE,1,pfp)==1)
{
if (strcmp (temp.pName,p.pName) == 0)
{
return 1;
}
}
return 0;
}
p.pName = nameCheck;
is wrong as you try to assign address of one array to another. What you probably want is to copy it.
Use strcpy() instead.
strcpy(p.pName, nameCheck);
while (checkProduct == 1)
Since checkProduct is a function, the above condition will always be false as the address of function won't be equal to 1. You can store the return value in another integer like this:
int value = checkProduct(nameCheck);
while (value == 1)
/* rest of the code */
Or rather simply:
while ( checkProduct(nameCheck) == 1 ) {
...
Note - I've not checked entire code, there might be other bugs apart from this one. Btw, if you are new to programming, you can start with small examples from textbooks and then work towards slightly complex stuff.
int checkProduct (char nameCheck[100])
Note that the type signature is a lie. The signature should be
int checkProduct(char *nameCheck)
since the argument the function expects and receives is a pointer to a char, or, to document it for the user that the argument should be a pointer to the first element of a 0-terminated char array
int checkProduct(char nameCheck[])
Arrays are never passed as arguments to functions, as function arguments, and in most circumstances [the exceptions are when the array is the operand of sizeof, _Alignof or the address operator &] are converted to pointers to the first element.
{
product temp;
p.pName = nameCheck;
Arrays are not assignable. The only time you can have an array name on the left of a = is initialisation at the point where the array is declared.
You probably want
strcpy(p.pName, nameCheck);
there.
rewind (pfp);
while (fread(&temp,STRUCTSIZE,1,pfp)==1)
{
if (strcmp (temp.pName,p.pName))
strcmp returns a negative value if the first argument is lexicographically smaller than the second, 0 if both arguments are equal, and a positive value if the first is lexicographically larger than the second.
You probably want
if (strcmp(temp.pName, p.pName) == 0)
there.
gets (nameCheck);
Never use gets. It is extremely unsafe (and has been remoed from the language in the last standard, yay). Use
fgets(nameCheck, sizeof nameCheck, stdin);
but that stores the newline in the buffer if there is enough space, so you have to overwrite that with 0 if present.
If you are on a POSIX system and don't need to care about portability, you can use getline() to read in a line without storing the trailing newline.
checkProduct (nameCheck);
You check whether the product is known, but throw away the result. Store it in a variable.
while (checkProduct == 1)
checkProduct is a function. In almost all circumstances, a function designator is converted into a pointer, hence the warning about the comparison between a pointer and an integer. You meant to compare to the value of the call you should have stored above.
{
printf ("Product Already Exists!\n Enter another!\n");
while (getchar() !='\n')
You read in characters without storing them. So you will never change the contents of nameCheck, and then be trapped in an infinite loop.
{
continue;
}
If the only statement in a loop body is continue;, you should leave the body empty.
}
p.pName = nameCheck;
Once again, you can't assign to an array.
Concerning the edit,
char *nameCheck;
nameCheck = "";
fgets(nameCheck,sizeof nameCheck, stdin);
you have changed nameCheck from an array to a pointer. That means that sizeof nameCheck now doesn't give the number of chars you can store in the array, but the size of a pointer to char, which is independent of what it points to (usually 4 on 32-bit systems and 8 on 64-bit systems).
And you let that pointer point to a string literal "", which is the reason for the crash. Attempting to modify string literals is undefined behaviour, and more often than not leads to a crash, since string literals are usually stored in a read-only segment of the memory nowadays.
You should have left it at
char nameCheck[100];
fgets(nameCheck, sizeof nameCheck, stdin);
and then you can use sizeof nameCheck to tell fgets how many characters it may read, or, alternatively, you could have a pointer and malloc some memory,
#define NAME_LENGTH 100
char *nameCheck = malloc(NAME_LENGTH);
if (nameCheck == NULL) {
// malloc failed, handle it if possible, or
exit(EXIT_FAILURE);
}
fgets(nameCheck, NAME_LENGTH, stdin);
Either way, after getting input, remove the newline if there is one:
size_t len = strlen(nameCheck);
if (len > 0 && nameCheck[len-1] == '\n') {
nameCheck[len-1] = 0;
}
// Does windows also add a '\r' when reading from stdin?
if (len > 1 && nameCheck[len-2] == '\r') {
nameCheck[len-2] = 0;
}

Resources