As far as SQL Server is concerned, column names must be specified explicitly. There is no way around. As far as you are concerned, names can be parametrised, but that means you need to generate the entire statement dynamically.
Depending on how often you need to do this, there may be no need to use dynamic SQL. If this is a one-off job, you could just generate the assignment list using something like
x.prev_name + ' = ' + c.name + ','
sys.columns AS c
CROSS APPLY (SELECT CONCAT('q', STUFF(c.name, 1, 1, '') - 1)) AS c (prev_name)
c.object_id = OBJECT_ID('dbo.tablename')
AND c.name BETWEEN 'q49' AND 'q79'
Run that in SSMS to get an output like this:
(No column name)
q48 = q49,
q49 = q50,
q50 = q51,
q78 = q79,
Copy the contents of the output column and paste it into the query window where you are writing your UPDATE statement, at the appropriate position. You will only need to make minimal syntactical adjustments, like removing the comma after the final assignment.
Even if I knew I might need to do this kind of data movement again in the future, I would still consider this approach first before actually going the dynamic SQL route.
If you firmly believe you need a more convenient tool for this problem, then you can use the above statement as a starting point for your script. Browse the dynamic-sql questions on this site or on Stack Overflow to find more examples to help you with the final solution.