Recently I've been learning about Strings and Pointers in C and have learned that you can do some pretty cool things in C using pointer arithmetic which reversing a String is one of these things. The following is the code that I am working with:
#include <stdio.h>
#include <string.h>
void print_reverse_string(char s[])
{
size_t length = strlen(s);
char *beginning = s;
char *end = beginning + length - 1;
while(end >= beginning)
{
printf("%c", *end);
end = end - 1;
}
}
int main()
{
printf("Please enter string to reverse: ");
char input_string[20];
fgets(input_string, 20, stdin);
/* Get rid of new line character from fgets function */
input_string[strlen(input_string) - 1] = '\0';
printf("Reversed string: ");
print_reverse_string(input_string);
return 0;
}
My concern begins with the following line of code:
char *end = beginning + length - 1;
This assumes that the ending memory location of an array will always be greater than the beginning memory location. Is this something that I should be concerned about as a C programmer or can I always be guaranteed that
&randomArray[0] < &randomArray[1] < &randomArray[2] < .... < &randomArray[lastElement]
It's just that I have been reading about different memory spaces and how certain spaces grow upwards while others grow downwards, for example, the stack growing downwards and the heap growing upwards, and thought that there might be a possibility of arrays growing downward in size.
Could this occur on a certain architecture or am I overthinking this possibility?
char *end = beginning + length - 1; leads to undefined behavior when length == 0. Example: "ending memory location of an array will always be greater" may be true expect code wants it to be false. Pointer arithmetic such as beginning + length - 1 is only valid from the beginning of an object to 1 object past its end. So beginning + 0 - 1 is UB.
Subsequent (like in an array) objects' addresses compare in increasing order - regardless of their underlying values, but the arithmetic is valid only in a narrow range.
Better to do
char *end = beginning + length;
while(end > beginning) {
end = end - 1;
printf("%c", *end);
}
Side issue: Should the first character read via fgets() is '\0', the below code attempts input_string[SIZE_MAX] = '\0'
// do not use
input_string[strlen(input_string) - 1] = '\0';
// alternatives
if (*input_string) input_string[strlen(input_string) - 1] = '\0';
// or
input_string[strcspn(input_string, "\n")] = '\0';
C arrays are always allocated contiguously in memory, from lowest address to highest.
This assumes that the ending memory location of an array will always be greater than the beginning memory location.
Yes, your assumption is correct. An array stores its elements in contiguous memory locations and in increasing order.
All answers above me were correct. When you write
char input_string[20];
you get 20*sizeof(char) bytes allocated on the stack, and input_string is only a pointer pointing to the first item, and it is handled like a pointer. For example input_string[10] means *(input_string + 10), etc.
Instead of repeating others, here's a tricky solution of the same task. Recursion in C is lovely for me, or black magic for the ignorants :) Takes some time to understand it, though. Not for serious use, but for fun!
The two backsides of recursion are: danger of stack overflow, and inefficiency. Neither of them are relevant to your question.
#include <stdio.h>
void reverse()
{
int c = getchar();
if (c != '\n' && c != EOF)
{
reverse();
putchar(c);
}
else
printf("Reversed string: ");
}
int main(void)
{
printf("Please enter string to reverse: ");
reverse();
return 0;
}
Related
Background:
I'm trying to create a program that takes a user name(assuming that input is clean), and prints out the initials of the name.
Objective:
Trying my hand out at C programming with CS50
Getting myself familiar with malloc & realloc
Code:
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
string prompt(void);
char *getInitials(string input);
char *appendArray(char *output,char c,int count);
//Tracks # of initials
int counter = 0;
int main(void){
string input = prompt();
char *output = getInitials(input);
for(int i = 0; i < counter ; i++){
printf("%c",toupper(output[i]));
}
}
string prompt(void){
string input;
do{
printf("Please enter your name: ");
input = get_string();
}while(input == NULL);
return input;
}
char *getInitials(string input){
bool initials = true;
char *output;
output = malloc(sizeof(char) * counter);
for(int i = 0, n = strlen(input); i < n ; i++){
//32 -> ASCII code for spacebar
//9 -> ASCII code for tab
if(input[i] == 32 || input[i] == 9 ){
//Next char after spaces/tab will be initial
initials = true;
}else{//Not space/tab
if(initials == true){
counter++;
output = appendArray(output,input[i],counter);
initials = false;
}
}
// eprintf("Input[i] is : %c\n",input[i]);
// eprintf("Counter is : %i\n",counter);
// eprintf("i is : %i\n",i);
// eprintf("n is : %i\n",n);
}
return output;
}
char *appendArray(char *output,char c,int count){
// allocate an array of some initial (fairly small) size;
// read into this array, keeping track of how many elements you've read;
// once the array is full, reallocate it, doubling the size and preserving (i.e. copying) the contents;
// repeat until done.
//pointer to memory
char *data = malloc(0);
//Increase array size by 1
data = realloc(output,sizeof(char) * count);
//append the latest initial
strcat(data,&c);
printf("Value of c is :%c\n",c);
printf("Value of &c is :%s\n",&c);
for(int i = 0; i< count ; i++){
printf("Output: %c\n",data[i]);
}
return data;
}
Problem:
The output is not what i expected as there is a mysterious P appearing in the output.
E.g When i enter the name Barack Obama, instead of getting the result:BO, i get the result BP and the same happens for whatever name i choose to enter, with the last initial always being P.
Output:
Please enter your name: Barack Obama
Value of c is :B
Value of &c is :BP
Output: B
Value of c is :O
Value of &c is :OP
Output: B
Output: P
BP
What i've done:
I've traced the problem to the appendArray function, and more specifically to the value of &c (Address of c) though i have no idea what's causing the P to appear,what it means, why it appears and how i can get rid of it.
The value of P shows up no matter when i input.
Insights as to why it's happening and what i can do to solve it will be much appreciated.
Thanks!
Several issues, in decreasing order of importance...
First issue - c in appendArray is not a string - it is not a sequence of character values terminated by a 0. c is a single char object, storing a single char value.
When you try to print c as a string, as in
printf("Value of &c is :%s\n",&c);
printf writes out the sequence of character values starting at the address of c until it sees a 0-valued byte. For whatever reason, the byte immediately following c contains the value 80, which is the ASCII (or UTF-8) code for the character 'P'. The next byte contains a 0 (or there's a sequence of bytes containing non-printable characters, followed by a 0-valued byte).
Similarly, using &c as the argument to strcat is inappropriate, since c is not a string. Instead, you should do something like
data[count-1] = c;
Secondly, if you want to treat the data array as a string, you must make sure to size it at least 1 more than the number of initials and write a 0 to the final element:
data[count-1] = 0; // after all initials have been stored to data
Third,
char *data = malloc(0);
serves no purpose, the behavior is implementation-defined, and you immediately overwrite the result of malloc(0) with a call to realloc:
data = realloc(output,sizeof(char) * count);
So, get rid of the malloc(0) call altogether; either just initialize data to NULL, or initialize it with the realloc call:
char *data = realloc( output, sizeof(char) * count );
Fourth, avoid using "magic numbers" - numeric constants with meaning beyond their immediate, literal value. When you want to compare against character values, use character constants. IOW, change
if(input[i] == 32 || input[i] == 9 ){
to
if ( input[i] == ' ' || input[i] == '\t' )
That way you don't have to worry about whether the character encoding is ASCII, UTF-8, EBCDIC, or some other system. ' ' means space everywhere, '\t' means tab everywhere.
Finally...
I know part of your motivation for this exercise is to get familiar with malloc and realloc, but I want to caution you about some things:
realloc is potentially an expensive operation, it may move data to a new location, and it may fail. You really don't want to realloc a buffer a byte at a time. Instead, it's better to realloc in chunks. A typical strategy is to multiply the current buffer size by some factor > 1 (typically doubling):
char *tmp = realloc( data, current_size * 2 );
if ( tmp )
{
current_size *= 2;
data = tmp;
}
You should always check the result of a malloc, calloc, or realloc call to make sure it succeeded before attempting to access that memory.
Minor stylistic notes:
Avoid global variables where you can. There's no reason counter should be global, especially since you pass it as an argument to appendArray. Declare it local to main and pass it as an argument (by reference) to getInput:
int main( void )
{
int counter = 0;
...
char *output = getInitials( input, &counter );
for(int i = 0; i < counter ; i++)
{
printf("%c",toupper(output[i]));
}
...
}
/**
* The "string" typedef is an abomination that *will* lead you astray,
* and I want to have words with whoever created the CS50 header.
*
* They're trying to abstract away the concept of a "string" in C, but
* they've done it in such a way that the abstraction is "leaky" -
* in order to use and access the input object correctly, you *need to know*
* the representation behind the typedef, which in this case is `char *`.
*
* Secondly, not every `char *` object points to the beginning of a
* *string*.
*
* Hiding pointer types behind typedefs is almost always bad juju.
*/
char *getInitials( const char *input, int *counter )
{
...
(*counter)++; // parens are necessary here
output = appendArray(output,input[i],*counter); // need leading * here
...
}
So I am working away on the 'less comfortable' version of the initials problem in CS50, and after beginning with very verbose code I've managed to whittle it down to this:
#include <cs50.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>
int c = 0;
int main(void)
{
string name = get_string();
int n = strlen(name);
char initials[10];
// first letter is always going to be the first initial
initials[0] = name[0];
// count through letters looking for spaces + add the first letter after a
// space to the initials array
for (int j = 0; j < n; j++)
{
if (name[j] == 32)
{
c += 1;
initials[c] += name[j+1];
}
}
// print out initials
for (int k = 0; k <= c; k++)
{
printf("%c", toupper(initials[k]));
}
printf("\n");
}
As it stands like that it passes, but I feel like I am copping out a little cos I just pick [10] out of the air for the initial array size which I know isn't good practice. To make it a little 'better' I've tried to run a 'for' loop to iterate through the name string and add up the number of spaces. I then want to make the array [spaces + 1] as if there are 2 spaces then there will be 3 initials. The code I am trying for that is:
string name = get_string();
int n = strlen(name);
for (int i = 0; i < n; i++)
{
if (name[i] == 32)
{
spaces +=1;
}
}
The thought is that I then make 'char initials[spaces + 1]' on the next line, but even before I can do that, compiling my code with just this 'for' loop returns a fail when I upload it for checking (although it compiles no problem). Even if I don't use any of the 'for' loops output the mere fact it is there gives me this error.
Where am I going wrong?
Any help on this would be much appreciated.
Thanks!
First of all, keep in mind that execution speed is most often more valuable than memory use. If you first go look for spaces and after that allocate memory, you have to iterate through the array twice. This is an optimization of memory use at the cost of execution speed. So it might make more sense to just allocate a "large enough" array of lets say 100 characters and keep the code that you have.
I then want to make the array [spaces + 1] as if there are 2 spaces then there will be 3 initials
Keep in mind that C strings are null terminated, so you need to allocate room for the null terminator too, spaces + 1 + 1.
compiling my code with just this 'for' loop returns a fail when I upload it for checking (although it compiles no problem). Even if I don't use any of the 'for' loops output the mere fact it is there gives me this error.
What error? Does it compile or does it not compile, your text is contradicting.
Make sure you initialize spaces to zero.
As a side note, never use "magic numbers" in C code. if (name[i] == 32), 32 is gibberish to anyone who can't cite the ASCII table by memory. In addition, it is non-portable to systems with other symbol tables that might not have the same index numbers. Instead write:
if (name[i] == ' ')
In my opinion, a good approach to cater for such situations is the one the library function snprintf uses: It requires you to pass in the string to fill and the size of that string. In ensures that the string isn't overwritten and that the string is zero-terminated.
The function returns the length of the characters written to the string if the had the string been large enough. You can now do one of two things: Guess a reasonable buffer size and accept that the string will be cut short occasionally. Or call the function with a zero length, use the return value to allocate a char buffer and then fill it with a second call.
Applying this approach to your initials problem:
int initials(char *ini, int max, const char *str)
{
int prev = ' '; // pretend there's a space before the string
int n = 0; // actual number of initials
while (*str) {
if (prev == ' ' && *str != ' ') {
if (n + 1 < max) ini[n] = *str;
n++;
}
prev = *str++;
}
if (n < max) {
ini[n] = '\0';
} else if (max > 0) {
ini[max] = '\0';
}
return n;
}
You can then either use the fixed-size bufer approach:
char *name = "Theodore Quick Brown Fox";
char ini[4];
initials(ini, sizeof(ini), name);
puts(ini); // prints "TQB", "F" is truncated
Or the two-step dynamic-size approach:
char *name = "Theodore Quick Brown Fox";
int n;
n = initials(NULL, 0, name);
char ini[n + 1];
initials(ini, sizeof(ini), name);
puts(ini); // prints "TQBF"
(Note that this implementation of initals will ignore multiple spaces and spaces at the end or at the beginning of the string. Your look-one-ahead function will insert spaces in these cases.)
You know your initials array can't be any bigger than the name itself; at most, it can't be more than half as big (every other character is a space). So use that as your size. The easiest way to do that is to use a variable-length array:
size_t n = strlen( name ); // strlen returns a size_t type, not int
char initials[n/2+1]; // n/2+1 is not a *constant expression*, so this is
// a variable-length array.
memset( initials, 0, n + 1 ); // since initials is a VLA, we can't use an initializer
// in the declaration.
The only problem is that VLA support may be iffy - VLAs were introduced in C99, but made optional in C2011.
Alternately, you can use a dynamically-allocated buffer:
#include <stdlib.h>
...
size_t n = strlen( name );
char *initials = calloc( n/2+1, sizeof *initials ); // calloc initializes memory to 0
/**
* code to find and display initials
*/
free( initials ); // release memory before you exit your program.
Although, if all you have to do is display the initials, there's really no reason to store them - just print them as you find them.
Like others have suggested, use the character constant ' ' instead of the ASCII code 32 for comparing against a space:
if ( name[j] == ' ' )
or use the isspace library function (which will return true for spaces, tabs, newlines, etc.):
#include <ctype.h>
...
if ( isspace( name[j] ) )
I'm studying software engineering, and came across this exercise: it asks to write a recursive function in C language that receives a positive integer and an empty string, and "translates" the number into a string. Meaning that after calling the function, the string we sent would contain the number but as a string of its digits.
I wrote this function, but when I tried printing the string, it did print the number I sent, but in reverse.
This is the function:
void strnum(int n, char *str)
{
if(n)
{
strnum(n/10, str+1);
*str = n%10 + '0';
}
}
For example, I sent the number 123 on function call, and the output was 321 instead of 123.
I also tried exchanging the two lines within the if statement, and it still does the same. I can't figure out what I did wrong. Can someone help please?
NOTE: Use of while and for loop statements is not allowed for the exercise.
Note: your current implementation design is somewhat dangerous since you have no way of knowing if you are really writing in valid memory; consider implementing the function with a passed in len to know when you shouldn't try to write anymore; ie. to prevent buffer overruns.
Introduction
The problem is that you are shaving off the least significant digit, but assigning it to the most significant position in the buffer pointed to by str.
You will need to have the "off shaving" and the "assigning" synchronized, so that the least significant digit is stored at the end - and not the beginning.
Hints
Easiest solution would be to do what you currently are doing, and then later reverse the buffer, but this will require far more assignments than what is actually required.
The recommended way is to calculate the number of digits in your string, by doing this you'll know at what offset the end will be, and start assigning the least significant digit at that position.
How do I determine the number of digits of an integer in C?
The hack
Another alternative is having the recursive call modify the current value of our pointer, effectively making it assign the right value - at the right offset.
This example is mostly included because it's "fun", there are (as mentioned) other paths to walk.
#include <stdio.h>
void strnum_impl (int n, char ** str) {
if (n) {
strnum_impl (n/10, str);
**str = n%10 + '0';
(*str)++;
}
}
void strnum (int n, char * str) {
if (n == 0) { *str++ = '0'; }
else { strnum_impl (n, &str); }
*str = '\0'; /* null-terminate */
}
int main () {
char buf[256];
strnum (10240123, buf);
printf (">%s<\n", buf);
return 0;
}
>10240123<
As #Robert Harvey commented, as well as others, code is determining the least rather than the most significant digit and placing it in str[0].
It did look like fun to implement, so the below well copes with the entire range of int including INT_MIN and arbitrary sized int.
static char *strnum_helper(int n, char *str) {
str[0] = '0' - n%10;
if (n < -9) {
return strnum_helper(n/10, str - 1);
}
return str;
}
void strnum(int n, char *str) {
char buf[(sizeof n * CHAR_BIT)/3 + 3]; // Sized to any size int
if (n < 0) {
*str++ = '-';
}
else {
n = -n; // By using negative numbers, do not attempt -INT_MIN
}
buf[sizeof buf - 1] = '\0';
strcpy(str, strnum_helper(n, &buf[sizeof buf - 2]));
}
#Filip Roséen - refp pointed out the value of passing in a size. The above strnum() could be adjusted per a size limitation.
I was trying to convert a decimal from [0, 255] to a 8 bit binary number, where each bit will be separated by a comma and a space. I tried the following (eventually it worked, except for the last bit does not require any separator ):
#include <stdio.h>
#include <stdlib.h>
char* atobin(int input) {
char *str = malloc(0);
int index, count = 0;
if (input > 255| input < 0) {
printf ("Input out of range. Abort.\n");
exit(EXIT_FAILURE);
}
for (index = 7; index >= 0; index--) {
*(str + (count++)) = (input >> index & 1) ? '1' : '0';
*(str + (count++)) = ',';
*(str + (count++)) = ' ';
}
*(str + count) = '\0';
return str;
}
int main(int argc, char *argv[]) {
printf("%s\n", atobin(atoi(argv[1])));
return EXIT_SUCCESS;
}
Now I have a few questions:
I used malloc(0); as far as I am concerned, it will
allocate no memory from the heap. So, how/ why is it working?
Is the declaration *(str + count) = '\0'; necessary?
Is there any way to optimize this code?
UPDATE
To carry on this experiment, I have taken the atobin function in to a .h file. This time it creates some problems.
Now I add my last question:
What should be minimum integer to be used for the parameter of malloc? Some trial-and-error method makes me guess it should me 512. Any idea?
From a malloc doc:
If size is zero, the return value depends on the particular library implementation (it may or may not be a null pointer), but the returned pointer shall not be dereferenced.
That it works for you is just luck. Try some random malloc and free afterwards and you will have a high probability - but no guarantee - that you will get a crash.
This null-terminates the string. Depends on how you want to use the string if you need it. The printf in your example needs it, because that's the only way for it to know when the string ends.
I'm sorry, I don't have time to take a closer look.
malloc(0) is valid to be used. what it returns is implementation defined. but what happens if you access a object through the pointer returned by malloc(0) is undefined behavoiur. you can read this related question.
In C since strings are character arrays terminated by \0 it is better to use *(str + count) = '\0'; statement to set the last character of string. It is working in this code but its better to set the end of string in code always. It may work if bits are set to 0 since it is same as terminating a string.
Write a function in C language that:
Takes as its only parameter a sentence stored in a string (e.g., "This is a short sentence.").
Returns a string consisting of the number of characters in each word (including punctuation), with spaces separating the numbers. (e.g., "4 2 1 5 9").
I wrote the following program:
int main()
{
char* output;
char *input = "My name is Pranay Godha";
output = numChar(input);
printf("output : %s",output);
getch();
return 0;
}
char* numChar(char* str)
{
int len = strlen(str);
char* output = (char*)malloc(sizeof(char)*len);
char* out = output;
int count = 0;
while(*str != '\0')
{
if(*str != ' ' )
{
count++;
}
else
{
*output = count+'0';
output++;
*output = ' ';
output++;
count = 0;
}
str++;
}
*output = count+'0';
output++;
*output = '\0';
return out;
}
I was just wondering that I am allocating len amount of memory for output string which I feel is more than I should have allocated hence there is some wasting of memory. Can you please tell me what can I do to make it more memory efficient?
I see lots of little bugs. If I were your instructor, I'd grade your solution at "C-". Here's some hints on how to turn it into "A+".
char* output = (char*)malloc(sizeof(char)*len);
Two main issues with the above line. For starters, you are forgetting to "free" the memory you allocate. But that's easily forgiven.
Actual real bug. If your string was only 1 character long (e.g. "x"), you would only allocate one byte. But you would likely need to copy two bytes into the string buffer. a '1' followed by a null terminating '\0'. The last byte gets copied into invalid memory. :(
Another bug:
*output = count+'0';
What happens when "count" is larger than 9? If "count" was 10, then *output gets assigned a colon, not "10".
Start by writing a function that just counts the number of words in a string. Assign the result of this function to a variable call num_of_words.
Since you could very well have words longer than 9 characters, so some words will have two or more digits for output. And you need to account for the "space" between each number. And don't forget the trailing "null" byte.
If you think about the case in which a 1-byte unsigned integer can have at most 3 chars in a string representation ('0'..'255') not including the null char or negative numbers, then sizeof(int)*3 is a reasonable estimate of the maximum string length for an integer representation (not including a null char). As such, the amount of memory you need to alloc is:
num_of_words = countWords(str);
num_of_spaces = (num_of_words > 0) ? (num_of_words - 1) : 0;
output = malloc(num_of_spaces + sizeof(int)*3*num_of_words + 1); // +1 for null char
So that's a pretty decent memory allocation estimate, but it will definitely allocate enough memory in all scenarios.
I think you have a few other bugs in your program. For starters, if there are multiple spaces between each word e.g.
"my gosh"
I would expect your program to print "2 4". But your code prints something else. Likely other bugs exist if there are leading or trailing spaces in your string. And the memory allocation estimate doesn't account for the extra garbage chars you are inserting in those cases.
Update:
Given that you have persevered and attempted to make a better solution in your answer below, I'm going to give you a hint. I have written a function that PRINTs the length of all words in a string. It doesn't actually allocate a string. It just prints it - as if someone had called "printf" on the string that your function is to return. Your job is to extrapolate how this function works - and then modify it to return a new string (that contains the integer lengths of all the words) instead of just having it print. I would suggest you modify the main loop in this function to keep a running total of the word count. Then allocate a buffer of size = (word_count * 4 *sizeof(int) + 1). Then loop through the input string again to append the length of each word into the buffer you allocated. Good luck.
void PrintLengthOfWordsInString(const char* str)
{
if ((str == NULL) || (*str == '\0'))
{
return;
}
while (*str)
{
int count = 0;
// consume leading white space
while ((*str) && (*str == ' '))
{
str++;
}
// count the number of consecutive non-space chars
while ((*str) && (*str != ' '))
{
count++;
str++;
}
if (count > 0)
{
printf("%d ", count);
}
}
printf("\n");
}
The answer is: it depends. There are trade-offs.
Yes, it's possible to write some extra code that, before performing this action, counts the number of words in the original string and then allocates the new string based on the number of words rather than the number of characters.
But is it worth it? The extra code would make your program longer. That is, you would have more binary code, taking up more memory, which may be more than you gain. In addition, it will take more time to run.
By the way, you have a memory leak in your program, which is more of a problem.
As long as none of the words in the sentence are longer than 9 characters, the length of your output array needs only to be the number of words in the sentence, multiplied by 2 (to account for the spaces), plus an extra one for the null terminator.
So for the string
My name is Pranay Godha
...you need only an array of length 11.
If any of the words are ten characters or more, you'll need to calculate how many extra char your array will need by determining the length of the numeric required. (e.g. a word of length 10 characters clearly requires two char to store the number 10.)
The real question is, is all of this worth it? Unless you're specifically required (homework?) to use the minimal space required in your output array, I'd be minded to allocate a suitably large array and perform some bounds checking when writing to it.